Question:
I need to save several objects using nested serializers using DRF.
Documentation about saving nested serializers seems to be incomplete.
Workarounds suggested in this and this answers do not seem to follow Python's encapsulation principle. As if we treat serializer as an object that encapsulates CRUD of a certain model, it is counter-intuitive to perform an object creation in serializer that "belongs" to another model.
In-detail explanation of a question:
Lets say we have two models:
class A:
content = models.CharField()
class B:
content = models.CharField()
relation = models.ForeignKey('A')
And two corresponding serializers:
class BSerializer(ModelSerializer):
class Meta:
model = B
fields = '__all__'
class ASerializer(ModelSerializer):
b_field = BSerializer(many=True)
class Meta:
model = A
fields = '__all__'
Sample of JSON that has to be saved:
{
"content": "sample",
"b_field": [
{
"content": "sample"
},
{
"content": "sample"
}
]
}
Using the workarounds suggested here and here, I have to override create method of ASerializer to smth like this:
class ASerializer(ModelSerializer):
b_field = BSerializer(many=True)
class Meta:
model = A
fields = '__all__'
def create(self, validated_data):
b_field_data = validated_data.pop('b_field', None)
instance = super().create(validated_data)
if b_field_data:
for obj in b_field_data:
B.objects.create(
content=obj.content,
relation=instance
)
return instance
However, as I've already pointed out above, it is kind of counter-intuitive to process class B instance creation in serializer that is responsible for class A.
What is even less obvious, is that in the above ASerializer.create method, the nested BSerializer, when accessed via self.fields['b_field'] appears to be not-instantiated. Thus, I eventually need to instantiate and validate it once again in order to access BSerializer.create:
class ASerializer(ModelSerializer):
b_field = BSerializer(many=True)
class Meta:
model = A
fields = '__all__'
def create(self, validated_data):
b_field_data = validated_data.pop('b_field', None)
instance = super().create(validated_data)
if b_field_data:
extra_instance = self.fields['b_field'](data=b_field_data)
if extra_instance.is_valid():
self.fields['b_field'].save(relation_id=instance.id)
# I'm skipping further logic of BSerializer.save to simplify explanation
return instance
I would appreciate any suggested workarounds of this issue.
Thank you.
References:
[1]: Writable nested serializers