EN VI

Python - How to allow null value on serializer when the field in the model is required?

2024-03-15 15:30:06
Python - How to allow null value on serializer when the field in the model is required?

Given the following payload, models.py, and serializers.py in 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

The problem I'm having right now is, when I tried to POST the payload, DRF complained that foo field in FooChild is required, which is understandable, the problem is, the id of the Foo exists only after I created the foo instance (on line 21). I tried to tell DRF to ignore the requirement to provide foo when creating FooChild to no avail (see line 2, 9, 10 above). How do I solve this problem?

Actually I have an idea to set null=True in the FooChild model, but the FooChild can't exist without Foo, so I figured this doesn't make any sense. Thx for the help

Solution:

To allow a null value on the foo field in the FooChild serializer while keeping the field as required in the model, you can modify your code as follows:

  1. In FooChildSerializer, remove the foo field declaration.
class FooChildSerializer(serializers.ModelSerializer):
    class Meta:
        model = FooChild
        fields = ["id", "quantity", "price_estimation", ...]
  1. In FooSerializer, update the foo_details field to handle the creation of FooChild instances after saving the Foo instance.
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

By setting required=False for the foo_details field in the serializer, it allows the field to be optional in the payload. In the create method, we extract the foo_details data from validated_data using pop, keeping it None if not present.

After creating the foo instance, we check if foo_details_data exists. If it does, we iterate over the data and create FooChild instances, associating them with the foo instance.

This way, you can create a Foo instance with or without providing foo_details in the payload. If foo_details are provided, they will be associated with the Foo instance after it is created.

Answer

Login


Forgot Your Password?

Create Account


Lost your password? Please enter your email address. You will receive a link to create a new password.

Reset Password

Back to login