Создание 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