Django Rest Framework Recursive Nested Parent Serialization

Question:

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?

Asked By: Ali Ankarali

||

Answers:

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.

Answered By: Windsooon

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
Answered By: Ali Ankarali

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)
Answered By: jpaulsen

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.

Answered By: jazzyoda5