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
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:
FooChildSerializer
, remove the foo
field declaration.class FooChildSerializer(serializers.ModelSerializer):
class Meta:
model = FooChild
fields = ["id", "quantity", "price_estimation", ...]
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.