Django REST Framework — Programatically get related entity and serializer

Image: infinite realities nexus point by TellOfVisions

Let’s say we have an entity nexus point, i.e. many other entities that have o2m relations to the same entity. Some use cases would be implementing a Note model that relates to Users, Events, Projects etc. Adding foreign keys is fine up until one point, but, since all but one of these foreign keys will be always None, let’s implement a model where the entity is retrieved programatically.

class Note(Date):
    ...
    entity_type = models.CharField(max_length=64)
    entity_id = models.IntegerField()

To get the related Note for a User, we simply filter by Note.objects.get(entity_type=User, entity_id=user.id), similarly for other entities.

But, when retrieving the Note from a REST API, I want the related entity properly serialized, not just the type and id fields. With Django REST Framework, this only takes a few lines. In serializers.py:

from django.apps import apps
from rest_framework import serializers
class NoteSerializer(serializers.ModelSerializer):
    ...
    entity = serializers.SerializerMethodField()
    def get_entity(self, obj):
        pk = obj.entity_id
        class_name = obj.entity_type
        model_class = apps.get_model('app', class_name)
        model = model_class.objects.get(pk=pk)
        serializer_class = class_name + 'Serializer'  # assumes the "ModelNameSerializer" is the name of the serializer class in the current file
        return globals()[serializer_class](model).data
class Meta:
        model = Note
        fields = (..., 'entity', ...)

Using the SerializerMethodField and defining a function prefixed with get_ will trigger the result of that function to be represented in the entity key of the parent NoteSerializer. Inside it, we simply retrieve our related model and serialize it using the ModelNameSerializer. This means a User will always be serialized as a User, an Event as an Event and a Project as a Project.

I’ve spent a couple of hours looking for this solution, so I hope it helps in similar use cases.

Leave a Reply

Your email address will not be published. Required fields are marked *