Создание Django Serializer с полями по умолчанию в Meta и параметрах запроса

Я пытаюсь написать сериализатор, который будет принимать динамические поля и добавлять их к ограниченному числу полей, указанных в Meta, но кажется, что нет метода "добавить обратно" поле в сериализатор после того, как оно было создано.

Динамические поля согласно документации Django

class DynamicFieldsModelSerializer(ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

class BldgLocationAndFilters(DynamicFieldsModelSerializer):
    latitude = fields.FloatField(source='lat_016_decimal')
    longitude = fields.FloatField(source='long_017_decimal')

    class Meta:
        model = Bldg
        fields = ('latitude', 'longitude')

Я хотел бы сделать что-то, что изменило бы DynamicFieldsModelSerializer так, чтобы поля могли быть добавлены к набору, который уже был отфильтрован, но похоже, что поля Meta переопределяют все, так что ничего не может быть добавлено обратно (поля могут быть только удалены

).

Псевдокод желаемого поведения:

class DynamicFieldsUnionModelSerializer(ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            new_fields = set(fields)
            existing = set(self.fields)
            unique_new = new_fields.union(existing) - existing
            for field_name in unique_new:
                self.fields.update(field_name)

Если бы BldgLocationAndFilters вызывалось как serializer = BldLocationAndFilters(fields=['type']), я бы ожидал, что результирующие возвраты будут иметь fields = ('latitude', 'longitude', 'type')

DynamicFieldsModelSerializer работает только на удаление существующих полей, потому что реализация зависит от полей, уже построенных в __init__. Вы можете добавить поля после __init__, но вам придется каким-то образом построить их снова (а не просто добавить имена).

Но один из способов поддержать это - переопределить метод сериализатора get_field_names, который работает с не встроенными именами полей:

class BldgLocationAndFilters(ModelSerializer):
    latitude = fields.FloatField(source='lat_016_decimal')
    longitude = fields.FloatField(source='long_017_decimal')

    class Meta:
        model = Bldg
        fields = ('latitude', 'longitude')

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        self._fields_to_add = kwargs.pop('fields', None)
        super().__init__(*args, **kwargs)

    def get_field_names(self, *args, **kwargs):
        original_fields = super().get_field_names(*args, **kwargs)
        if self._fields_to_add:
            return set(list(original_fields) + list(self._fields_to_add))
        return original_fields


# Should use latitude, longitude, and type
BldgLocationAndFilters(instance, fields=('type',)).data

Обратите внимание, что здесь используется только ModelSerializer.

Или просто определите свой сериализатор с помощью __all__ (по-прежнему используя DynamicFieldsModelSerializer) и задайте поля по необходимости:

class BldgLocationAndFilters(DynamicFieldsModelSerializer):
    latitude = fields.FloatField(source='lat_016_decimal')
    longitude = fields.FloatField(source='long_017_decimal')

    class Meta:
        model = Bldg
        fields = '__all__'


BldgLocationAndFilters(instance, fields=('latitude', 'longitude', 'type')).data
Вернуться на верх