#django #django-rest-framework
Вопрос:
Я использую сериализаторы Django для создания API, из которого я как читаю, так и записываю.
Модели:
class Biomarker(models.Model):
low_value_description = models.TextField(blank=True, null=True)
high_value_description = models.TextField(blank=True, null=True)
class BiomarkerReading(models.Model):
biomarker = models.ForeignKey(Biomarker, on_delete=models.CASCADE)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
value = models.DecimalField(max_digits=30, decimal_places=8, default=0)
Сериализатор:
class BiomarkerReadingSerializer(serializers.ModelSerializer):
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
Формат JSON:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"value": 0.001
}
Все вышеперечисленное работает, и я могу читать и записывать в него в этом формате JSON. Однако теперь мне нужно добавить некоторые поля из родительской модели, чтобы ответ выглядел так:
{
"id": 617188,
"test" 71829,
"biomarker": {
"id": 32,
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
У меня есть часть чтения, работающая с использованием этих сериализаторов:
class BiomarkerDescriptionSerializer(serializers.ModelSerializer):
class Meta:
model = Biomarker
fields = ('id', 'low_value_description', 'high_value_description')
class BiomarkerReadingSerializer(serializers.ModelSerializer):
biomarker = BiomarkerDescriptionSerializer()
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
Однако я не могу найти способ записи в него, используя старый формат json (с "biomarker": 32
помощью JSON).
Я думал , что мне нужно будет что-то сделать в validate
или create
, но я получаю ответ 400 еще до того, как он попадет в эти методы:
class BiomarkerReadingSerializer(serializers.ModelSerializer):
... # as above
def validate(self, data):
print('validate') # Doesn't print
data = super().validate(data)
return data
def create(self, validated_data):
print('create') # Doesn't print
return super().create(validated_data)
Пример в документах для сериализаторов с возможностью записи и другие примеры, которые я нашел, поэтому обсуждают только случай создания дочерних записей при записи в сериализатор родительских записей, а не наоборот.
Я не хочу create
использовать родительский биомаркер через API, мне просто нужно иметь возможность ссылаться на него pk/id
во входящем JSON, как и раньше.
Я не возражаю, если мне придется изменить имена ключей на что — то подобное для входящих:
{
"id": 617188,
"test" 71829,
"biomarker_id": 32,
"value": 0.001
}
Или что-то в этом роде для ответа:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"descriptions": {
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
Если это облегчит дело.
Ответ №1:
Я ненавижу отвечать на свой собственный вопрос, но решение заключалось в подклассе serializers.RelatedField
, который объясняется в расширенном использовании сериализатора).
Это позволяет отдельно управлять тем, как значение(экземпляр) представляется как сериализованное, и как сериализованные данные используются для извлечения или создания значения (экземпляра).
class BiomarkerDescriptionSerializer(serializers.RelatedField):
def to_representation(self, value):
data = {}
for field in ('id', 'low_value_description', 'high_value_description'):
data[field] = getattr(value, field)
return data
def to_internal_value(self, data):
return Biomarker.objects.get(id=data)
def get_queryset(self, *args):
pass
class BiomarkerReadingSerializer(serializers.ModelSerializer):
biomarker = BiomarkerDescriptionSerializer()
...
Переопределенное get_queryset
требуется, даже если оно ничего не делает.
Это означает, что поступающие данные выглядят следующим образом:
{
"id": 617188,
"test" 71829,
"biomarker": 32,
"value": 0.001
}
Тем не менее, выходящие данные выглядят так:
{
"id": 617188,
"test" 71829,
"biomarker": {
"id": 32,
"low_value_description": "All good",
"high_value_description": "You will die",
},
"value": 0.001
}
Спасибо тем, кто предложил ответы, очень признателен
Ответ №2:
Использование depth = 1
опции решит вашу проблему.
class BiomarkerReadingSerializer(serializers.ModelSerializer):
class Meta:
model = BiomarkerReading
fields = (
'id', 'test', 'biomarker', 'value'
)
depth = 1
Взгляните: https://www.django-rest-framework.org/api-guide/serializers/#specifying-nested-serialization
Комментарии:
1. К сожалению, поле
test
является FK, и мне действительно нужно связать его с родителем.