I have two serializers organised like this:
class OuterSerializer():
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
class InnerSerializer():
field_1 = CharField()
field_2 = CharField()
Now my use case is to partial update the outer serializer's model. How I'm doing that is:
def partial_update(self, request, *args, **kwargs):
serializer = OuterSerializer(data=request.data, context={'request': self.request}, partial=True)
serializer.is_valid(raise_exception=True)
data = serializer.data
outerobj = self.service_layer.update(kwargs['pk'], data, request.user)
response_serializer = OpportunitySerializer(instance=outerobj, context={'request': self.request})
return Response(response_serializer.data, HTTPStatus.OK)
The issue is this partial flag does not get passed down to the InnerSerializer. For example if my request body looks like below, I want it to work:
{"inner_obj":
{
"field_1" : "abc"
}
}
Currently I get a 400 error for this saying the field is required.
What I've tried :
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# We pass the "current serializer" context to the "nested one"
self.fields['inner_obj'].context.update(self.context)
self.fields['inner_obj'].partial = kwargs.get('partial')
However this doesn't travel down.
Try to modify the InnerSerializer
so that it could accept the partial
argument and pass it to its parent, like following:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def __init__(self, *args, **kwargs):
self.partial = kwargs.pop('partial', False)
super().__init__(*args, **kwargs)
class OuterSerializer(serializers.Serializer):
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
def __init__(self, *args, **kwargs):
partial = kwargs.get('partial')
super().__init__(*args, **kwargs)
self.fields['inner_obj'].child.partial = partial
You can also override the to_internal_value()
method in the InnerSerializer
to make it accept partial
updates so:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def to_internal_value(self, data):
if self.partial:
return {field: data.get(field, getattr(self.instance, field)) for field in data}
return super().to_internal_value(data)
class OuterSerializer(serializers.Serializer):
inner_obj = InnerSerializer(many=True, required=False)
other fields ......
For the error:
KeyError: "Got KeyError when attempting to get a value for field field_2
on serializer
InnerSerializer`.
The error message you're encountering suggests that the serializer is trying to access the value for field_2
from the data, but it's not present.
Currently to solve the error, you should override the to_representation()
method in the InnerSerializer
to only include the fields that are present so:
class InnerSerializer(serializers.Serializer):
field_1 = CharField()
field_2 = CharField()
def to_representation(self, instance):
data = super().to_representation(instance)
return {field: value for field, value in data.items() if value is not None}