Как мне написать вложенному родителю в сериализаторе Django

#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, и мне действительно нужно связать его с родителем.