Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Passing partial=True down to nested serializer in DRF

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

{"inner_obj":
  {
    "field_1" : "abc"
  }
}

Currently I get a 400 error for this saying the field is required.

What I’ve tried :

  1. Setting the partial variable within the OuterSerializer in the init method by modifying it as such
    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.

>Solution :

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

Another possible solution.

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 ......

Edit:

For the error:

KeyError: "Got KeyError when attempting to get a value for field field_2on serializerInnerSerializer`.

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}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading