Django: одно пользовательское поле модели для двух столбцов БД

avatar
Gonzalo
1 июля 2021 в 16:47
126
1
0

Я хотел бы создать настраиваемое поле, которое получает строку даты и времени и хранит данные в двух столбцах базы данных. Я читаю django doc, но не понимаю, как это сделать с более чем одним столбцом db.

Это устаревшая БД, поэтому я не могу изменить расположение таблиц БД.

У меня сейчас есть эта модель.

class MyModel(models.Model):
    mixing_datetime = models.DateTimeField()
    mixing_datetime_utc_offset_seconds = models.IntegerField()

Я хотел бы создать поле, которое позволяет внешнему интерфейсу (это API с DRF) просто отправлять строку даты и времени (вместо даты и времени и смещения отдельно), а серверу выполнять работу за кулисами. Таким образом, идея будет такой:

class MyModel(models.Model):
    mixing_datetime = models.DateTimeField()
    mixing_datetime_utc_offset_seconds = models.IntegerField()
    datetime = MyCustomDatetimeField() # This also should not be sent to db

Итак, чтобы уточнить, идея заключается в том, чтобы интерфейс пользователя выполнял этот запрос:

frontend_client.post('server_url', payload={'datetime': '2021-07-02T14:34:49+00:00'}

А затем серверная часть выполнит некоторые расчеты и установит параметры Mixing_datetime и Mixing_datetime_utc_offset_seconds

Как этого достичь?

Источник
Brian Destura
2 июля 2021 в 04:07
0

Что именно бэкэнд делает за кулисами? Вы хотите обработать эту строку даты и времени из внешнего интерфейса и сохранить ее в mixing_datetime и mixing_datetime_utc_offset_seconds?

Gonzalo
2 июля 2021 в 15:30
0

@bdbd Я добавляю больше контекста. Внешний интерфейс просто отправляет строку. Обработка будет выполняться в бэкэнде

Brian Destura
5 июля 2021 в 23:58
0

Хорошо, теперь я понимаю вашу ситуацию. Я удалил свой ответ, так как он не отвечает на ваш вопрос. Но да, настраиваемые поля модели django предназначены для настраиваемых полей, которые в конечном итоге будут отражены в базе данных, поэтому, если вы хотите сделать это таким образом, вам, вероятно, придется много переопределять.

Brian Destura
6 июля 2021 в 00:03
0

Вероятно, вы можете просто написать метод (в классе модели или в качестве примеси), который принимает строку даты и времени и обрабатывает ее для этих двух полей модели. Это должно быть довольно просто обрабатывать в сериализаторах/представлении

Ответы (1)

avatar
Gonzalo
6 июля 2021 в 17:50
0

Наконец-то я нашел это решение. Это не то, о чем я спрашивал (настраиваемое поле моделей django), но поскольку это API, что я делаю, я решил создать настраиваемое поле сериализатора DRF. Если вы не работаете с DRF, вероятно, вы можете перезаписать метод сохранения модели.

Это настраиваемое поле сериализатора DRF.

class CustomDateTimeField(serializers.Field):
    """Custom field used to convert a string in isoformat into other two fields.
    So the caller instead of sending datetime and offset, will only send the string, and internally we 
    are going to split it in the two respective fields.
    Same when caller requests data, it will receive only one datetime field. (output pf to_representation).

    In a model we are going to have two fields that will follow this naming pattern:
        >> datetime = models.DateTimeField()
        >> offset_seconds = models.IntegerField()
    """

    def __init__(self, **kwargs):
        self.prefix_field_name = kwargs.pop("prefix_field_name")
        self.datetime_field = 'datetime'
        self.offset_seconds_field = 'offset_seconds'

        super().__init__(**kwargs)
        # Set this, so we receive all model fields in to_representation.
        self.source = '*'

    def to_representation(self, instance):
        """This function is called when an object instance is serialized. For example an instance retrieved from db.
        For example this serialization will lead into this function:
            >> instance = MyModel.objects.last()
            >> MyModelSerializer(instance).data
        """
        return get_isoformat_string_from_datetime(
            getattr(instance, self.datetime_field), getattr(instance, self.offset_seconds_field)
        )

    def to_internal_value(self, data):
        """This functions is called when a json object is serialized. For example on an Api call.
        The data returned from this function will be used to update the 'validated_data' dictionary.
        For example:
            >> data_to_be_serialized = {'datetime': '2021-04-10T07:51:15.958266-01:00', 'other_field': 10}
            >> MyModelSerializer(data=data_to_be_serialized)
        """
        if not isinstance(data, str):
            raise serializers.ValidationError("Incorrect type. Expected a string")
        if not is_in_isoformat(data):
            raise serializers.ValidationError("Data should be in isoformat")
        datetime = get_datetime_from_string(data)
        # This fields are going to be used to update validated_data dict.
        ret = {
            self.datetime_field: datetime,
            self.offset_seconds_field: get_utc_offset_seconds(datetime),
        }
        return ret

Использование:

class TestModel(models.Model):
    datetime = models.DateTimeField()
    offset_seconds = models.IntegerField()

class MyModelSerializer(serializers.ModelSerializer):
    datetime = CustomDateTimeField()

    class Meta:
        model = TestModel
        fields = ('datetime', )