menu

Questions & Answers

Django Rest Framework Recursive Nested Parent Serialization

I have a model with a self referential field called parent. Model:

class Zone(BaseModel):
    name = models.CharField(max_length=200)
    parent = models.ForeignKey('self', models.CASCADE, blank=True, null=True, related_name='children')

    def __unicode__(self):
        return self.name

Serializer:

class ZoneSerializer(ModelSerializer):
    parent = PrimaryKeyRelatedField(many=False, queryset=Zone.objects.all())
    parent_disp = StringRelatedField(many=False, source="parent")

    class Meta:
        model = Zone
        fields = ('id', 'name', 'parent', 'parent_disp')

Now I want to serialize the parent of the zone and its parent and its parent till parent is none. I found recursive serialization methods for children but not for parent. How can I do this?

Answers(4) :

Try use SerializerMethodField here:

def get_parent(self, obj):
    # query what your want here.

I'm not sure D-R-F has build-in methods for this, but you can use query to get what you want in this method.

Comments:
2023-01-23 00:55:08
Well but I have to serialize the parent. So I need a recursive serializer.

Ok, I got it working like that.

class ZoneSerializer(ModelSerializer):
    parent = SerializerMethodField()

    class Meta:
        model = Zone
        fields = ('id', 'name', 'project', 'parent',)

    def get_parent(self, obj):
        if obj.parent is not None:
            return ZoneSerializer(obj.parent).data
        else:
            return None

Just wanted to add an additional solution that worked better for me.

The answer above using the SerializerMethodField and then instantiating a new serializer class works nicely, but if you are trying to serialize a larger nested dataset instantiating a new serializer for each of your nested objects might become quite slow.

Trying to use a single instance of the serializer for all the nested objects made it a lot faster.

class ZoneSerializer(ModelSerializer):
    ...

    def get_parent(self, obj):
        if obj.parent is not None:
            return self.to_representation(obj)
        else:
            return None

One more step could be to also convert your return of the serializer method field to a ReturnDict instance, which is what a serializer normally does when you call serializer.data property.

from rest_framework.utils.serializer_helpers import ReturnDict

class ZoneSerializer(ModelSerializer):
    ...

    def get_parent(self, obj):
        if obj.parent is not None:
            return ReturnDict(self.to_representation(obj), serializer=self)
        else:
            return None

There are some potential disadvantages of this approach of course, like loosing validation that happens when instantiating a serializer, harder to modify for nested objects, etc. If you are confident in your serializer setup and data being passed to the serializer this can be a nice optimization.

You also can do:

class ZoneSerializer(ModelSerializer):
    class Meta:
        model = Zone
        fields = ('id', 'name', 'project', 'parent',)

    def to_representation(self, instance):
        self.fields['parent'] = ZoneSerializer(read_only=True)
        return super(ZoneSerializer, self).to_representation(instance)
Comments:
2023-01-23 00:55:08
this also works nicely for children through related name