Как разрешить нулевое значение в сериализаторе, если поле в модели является обязательным?

Заданы следующие payload, models.py и serializers.py в Django (DRF):

payload

{
  "created_by": 6,
  "brand": 1,
  "foo_details": [
    {
      "quantity": 123,
      "price_estimation": 456
    },
    {
      "quantity": 789,
      "price_estimation": 1011
    }
  ]
}

models.py

class Foo(models.Model):
    created_by = models.ForeignKey(CustomUser, on_delete=models.PROTECT)
    brand = models.ForeignKey(Brand, on_delete=models.SET_NULL, null=True)
    # OTHER FIELDS HERE

class FooChild(models.Model):
    foo = models.ForeignKey(Foo, on_delete=models.CASCADE, related_name="foo_details")
    quantity = models.PositiveIntegerField(default=0)
    price_estimation = models.PositiveIntegerField(default=0)
    # OTHER FIELDS HERE

serializers.py

class FooChildSerializer(serializers.ModelSerializer):
    # foo = serializers.PrimaryKeyRelatedField(read_only=True, required=False) -> LINE 2

    class Meta:
        model = FooChild
        fields = ["id", "foo", "quantity", "price_estimation", ...]

class FooSerializer(serializers.ModelSerializer):
    # foo_details = FooChildSerializer(many=True) -> LINE 9
    # foo_details = serializers.DictField(child=serializers.CharField(), many=True) -> LINE 10

    class Meta:
        model = Foo
        fields = ["id", "created_by", "brand", "foo_details", ...]

    def create(self, validated_data):
        # I want to save the payload to `Foo` and `FooChild` inside this function at the same time, below is my unsuccessful attempt

        # print(validated_data)
        # validated_data.pop("foo_details")
        # foo = Foo.objects.create(**validated_data) -> LINE 21
        # foo_child = FooChild.objects.create(foo=foo)
        
        # return foo

Проблема, с которой я столкнулся, заключается в том, что при попытке POST полезной нагрузки DRF пожаловался, что поле foo в FooChild является обязательным, что вполне понятно, проблема в том, что id в Foo существует только после того, как я создал экземпляр foo (в строке 21). Я пытался сказать DRF игнорировать требование предоставить foo при создании FooChild, но безрезультатно (см. строки 2, 9, 10 выше). Как решить эту проблему?

Вообще-то у меня есть идея установить null=True в модель FooChild, но FooChild не может существовать без Foo, поэтому я решил, что в этом нет никакого смысла. Спасибо за помощь

Чтобы разрешить нулевое значение для поля foo в сериализаторе FooChild и при этом сохранить поле как необходимое в модели, вы можете изменить свой код следующим образом:

  1. В FooChildSerializer удалите объявление поля foo.
class FooChildSerializer(serializers.ModelSerializer):
    class Meta:
        model = FooChild
        fields = ["id", "quantity", "price_estimation", ...]
  1. В FooSerializer обновите поле foo_details для обработки создания FooChild экземпляров после сохранения Foo экземпляра.
class FooSerializer(serializers.ModelSerializer):
    foo_details = FooChildSerializer(many=True, required=False)

    class Meta:
        model = Foo
        fields = ["id", "created_by", "brand", "foo_details", ...]

    def create(self, validated_data):
        foo_details_data = validated_data.pop("foo_details", None)

        foo = Foo.objects.create(**validated_data)

        if foo_details_data:
            for foo_detail_data in foo_details_data:
                FooChild.objects.create(foo=foo, **foo_detail_data)

        return foo

Установив required=False для поля foo_details в сериализаторе, мы позволяем полю быть необязательным в полезной нагрузке. В методе create мы извлекаем данные foo_details из validated_data с помощью pop, сохраняя их None, если они отсутствуют.

После создания экземпляра foo мы проверяем, существует ли foo_details_data. Если существует, мы перебираем данные и создаем экземпляры FooChild, связывая их с экземпляром foo.

Таким образом, вы можете создать экземпляр Foo с указанием или без указания foo_details в полезной нагрузке. Если foo_details предоставлены, они будут ассоциированы с экземпляром Foo после его создания.

Вернуться на верх