#django #django-rest-framework
#django #django-rest-framework
Вопрос:
Я хочу создать mixin для дополнительных полей в Modelserializer. Пожалуйста, обратитесь к приведенному ниже коду
class AdditionalFieldsMixin(object):
additional_fields = dict()
def __init__(self, *args, **kwargs):
super(AdditionalFieldsMixin, self).__init__(*args, **kwargs)
for field_name, field_instance in self.additional_fields.items():
self.fields[field_name] = field_instance
print(self.fields)
def additional_field_before_create(self, additional_field_data):
pass
def additional_field_after_create(self, additional_field_data, instance):
pass
def additional_field_before_update(self, additional_field_data):
pass
def additional_field_after_update(self, additional_field_data, instance):
pass
def create(self, validated_data):
additional_field_data = {}
for additional_field in self.additional_fields.keys():
additional_field_data[additional_field] = validated_data.pop(additional_field, None)
self.additional_field_before_create(additional_field_data)
instance = super(AdditionalFieldsMixin, self).create(validated_data)
self.additional_field_after_create(additional_field_data, instance)
return instance
def update(self, instance, validated_data):
additional_field_data = {}
for additional_field in self.additional_fields.keys():
additional_field_data[additional_field] = validated_data.pop(additional_field, None)
self.additional_field_before_update(additional_field_data)
instance = super(AdditionalFieldsMixin, self).update(instance, validated_data)
self.additional_field_after_update(additional_field_data, instance)
return instance
В основном классе я объявил
additional_fields = dict(
reference=CharField(write_only=True, allow_blank=True, allow_null=True, default=None),
)
но при получении данных в режиме списка это выдает ошибку
AssertionError: It is redundant to specify `source='reference'` on field 'CharField' in serializer 'MySerializer', because it is the same as the field name. Remove the `source` keyword argument.
почему это происходит?
также я отметил, что после первого запуска runserver он не выдает эту ошибку, но если я снова запрашиваю, он выдает ошибку во второй раз и так далее.
и еще один общий вопрос
как правильно добавлять поля в класс seialiser mixin? т.е. self.fields[field_name] = field_instance — правильный способ сделать это?
Ответ №1:
Вы можете это сделать, потому что, если вы используете modelerializer, вы не можете запросить
class Name(serializers.Serializer):
name = serializers.CharField(required=True)
...
def create(self, validated_data):
...
def update(self, instance, validated_data):
...
Ответ №2:
Дополнительные поля чтения и записи в Modelserializer
Дополнительные поля чтения и записи должны быть получены из экземпляра модели, чтобы вы могли добавить @property
или def reference
в модель и объявить поле в сериализаторе как
reference = CharField(source='reference')
и извлеките данные поля из проверенных данных, переопределив создание обновления modelserializer.
Короче говоря, дополнительное поле для чтения и записи очень сложно реализовать в прямом коде, НО ЭТО ВОЗМОЖНО.
Дополнительные поля только для чтения в Modelserializer
Используйте SerializerMethodField
, ReadOnlyField
с source
аргументом или аналогичный приведенный выше код с read_only=True
аргументом. например
reference = CharField(source='reference', read_only=True)
Дополнительные поля только для записи в Modelserializer (обязательный ответ)
Запись поля в mainserializer как
reference = CharField(write_only=True)
нормально, поле будет отображаться в проверенных данных, create
и update
поэтому
переопределите их, откройте поле, сделайте то, что вам нужно с ним делать, сделайте super()
вызов с новыми проверенными данными.
Создайте mixin, как указано в вопросе (для полей только для записи)
Оказывается, вы можете переопределить to_internal_value
метод serializer и добавить к нему поля. вызывается to_internal_value
, когда сериализатор сохраняет данные (создает, обновляет), поэтому вы можете переопределить его следующим образом
def to_internal_value(self, data):
for field_name, field_instance in self.additional_write_only_fields.items():
self.fields[field_name] = field_instance
return super(AdditionalWriteOnlyFieldsMixin, self).to_internal_value(data)
это решило бы проблему добавления многих полей только для записи в model serializer. вот почему я создал mixin следующим образом
class AdditionalWriteOnlyFieldsMixin(object):
additional_write_only_fields = dict()
def to_internal_value(self, data):
for field_name, field_instance in self.additional_write_only_fields.items():
self.fields[field_name] = field_instance
return super(AdditionalWriteOnlyFieldsMixin, self).to_internal_value(data)
def additional_fields_before_create(self, additional_data):
pass
def additional_fields_after_create(self, additional_data, instance):
pass
def additional_fields_before_update(self, additional_data):
pass
def additional_fields_after_update(self, additional_data, instance):
pass
def create(self, validated_data):
additional_data = {key: validated_data.pop(key, None) for key in self.additional_write_only_fields.keys()}
self.additional_fields_before_create(additional_data)
instance = super(AdditionalWriteOnlyFieldsMixin, self).create(validated_data)
self.additional_fields_after_create(additional_data, instance)
return instance
def update(self, instance, validated_data):
additional_data = {key: validated_data.pop(key, None) for key in self.additional_write_only_fields.keys()}
self.additional_fields_before_update(additional_data)
instance = super(AdditionalWriteOnlyFieldsMixin, self).update(instance, validated_data)
self.additional_fields_after_update(additional_data, instance)
return instance
Поэтому включите mixin в свой класс modelserializer и объявите additional_write_only_fields
как dict с ключом в качестве имени поля и значением в качестве экземпляра поля и переопределите additional_fields_*
функцию для реализации желаемой функциональности, например
class MySerializer(AdditionalWriteOnlyFieldsMixin, ModelSerializer):
additional_write_only_fields = dict(
reference=CharField(write_only=True, allow_blank=True, allow_null=True, default=None),
)
def additional_fields_after_create(self, additional_data, instance):
print(additional_data)
class Meta:
model=MyModel
fields='__all__'
Дополнительные примечания
Вы также можете переопределить to_representation
метод сериализатора для добавления полей только для чтения или создать комбинацию to_representation
и to_internal_value
, чтобы создать дополнительное поле для чтения и записи !. но я оставляю эту работу для другого ответа.
Ссылки