Сериализация родовых отношений с Django Rest Framework с поддержкой записи
У меня есть следующая модель:
class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
Я пытаюсь сериализовать эту модель таким образом, чтобы я мог назначить объект контента через конечную точку API
Пока я сделал это:
class TaggedItemSerializer(serializers.ModelSerializer):
content_object = serializers.RelatedField(read_only=True)
class Meta:
model = TaggedItem
Однако это только для чтения. Если я удаляю параметр read_only, я должен указать набор запросов для поля. Тем не менее, у меня есть много разных типов моделей для этого родового отношения. Кажется, что я дублирую код, если я указываю все возможные типы моделей как внутри сериализатора, так и в других местах модели.
Я мог бы также установить объект содержимого через поля object_id и content_type, но при этом я получаю сообщение об ошибке.
Например:
{
...
object_id: 1,
content_type: 'auth.User'
}
возвращает ответ 400 с "detail": "JSON parse error - Expected object or value"
Как я могу сделать этот content_object доступным для записи через API DRF?
1 ответ
Переопределить .to_internal_value
, .validate
а также .create
методы как это:
from django.apps import apps
class TaggedItemSerializer(serializers.ModelSerializer):
class Meta:
model = TaggedItem
read_only_fields = ('content_type', 'object_id', 'content_object')
def to_internal_value(self, data):
object_id = data.pop('object_id')
content_type = data.pop('content_type')
ret = super(ConfigCalcSerializer, self).to_internal_value(data)
ret['object_id'] = object_id
ret['content_type'] = content_type
return ret
def validate(self, data):
object_id = data.pop('object_id')
content_type = data.pop('content_type')
Model = apps.get_model(content_type)
try:
content_object = Model.objects.get(id=object_id)
except Model.DoesNotExist:
raise serializers.ValidationError('Not found')
else:
data['content_object'] = content_object
return data
def create(self, validate_data):
return TaggedItem.objects.create(**validate_data)