Search code examples
pythondjangodjango-rest-frameworkgeneric-relations

Django DRF and generic relations: how to obtain content_object field from API response?


I have implemented Generic Relations in Django DRF following the official guide and it works well, apart from the fact that I cannot seem to obtain the field content_object from my API response.

Basically, I have a model called Document that can either be related to a model called Folder or a model called Collection.

class Document(models.Model):

    limit = models.Q(app_label='folders', model='folder') | models.Q(
        app_label='collections', model='collection')

    title = models.CharField(max_length=500)
    # Field necessari per la Generic Relation
    content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=limit)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    content_object = GenericForeignKey(
        'content_type', 'object_id')
    category = models.CharField(max_length=30, blank=True, null=True)

    def __str__(self):
        return self.title


class Folder(models.Model):
    ...
    documents = GenericRelation(Document)

    def __str__(self):
        return self.title

class Collection(models.Model):
    ...
    documents = GenericRelation(Document)

    def __str__(self):
        return self.title


Here are my serializers:

class ContentObjectRelatedField(serializers.RelatedField):

    def to_representation(self, value):

        if isinstance(value, Folder):
            serializer = FolderSerializer(value)
        elif isinstance(value, Collection):
            serializer = CollectionSerializer(value)
        else:
            raise Exception('Unexpected type of object')

        return serializer.data


class DocumentSerializer(serializers.ModelSerializer):

    class Meta:
        model = Document
        fields = ('id', 'title', 'content_type',
                  'object_id', 'category')


class FolderSerializer(serializers.ModelSerializer):
    documents = DocumentSerializer(many=True, read_only=True)

    class Meta:
        model = Folder
        fields = ("id", "title", "description",
                  "documents")
        depth = 1

(Collection serializer is essentially the same ad the Folder serializer, with its own fields).

I was expecting to be able to access the content of the field content_object when retrieving - with a GET request to the API endpoint - the documents. Instead, that field is not available. If I do try to add it to the fields listed in its serializers, it throws an error.

How can I access that content so that I know, for each document, to what folder or what collection is belongs exactly?

Thanks a lot.


Solution

  • Try this:

    class ContentObjectRelatedField(serializers.RelatedField):
        def to_representation(self, value):
            if isinstance(value, Folder):
                serializer = FolderForDocumentSerializer(value)
            elif isinstance(value, Collection):
                serializer = CollectionForDocumentSerializer(value) # Defines CollectionForDocumentSerializer in the same manner of FolderForDocumentSerializer
            else:
                raise Exception('Unexpected type of object')
    
            return serializer.data
    
    
    class FolderForDocumentSerializer(serializers.ModelSerializer):
        class Meta:
            model = Folder
            fields = ("id", "title", "description")
            depth = 1
    
    
    class DocumentSerializer(serializers.ModelSerializer):
        content_object = ContentObjectRelatedField(read_only=True)
       
        class Meta:
            model = Document
            fields = ('id', 'title', 'content_object', 'category')
    
    # Note that you can use DocumentSerializer and CollectionSerializer, but not in ContentObjectRelatedField.to_representation
    

    Your frontend can deduct the type of content_object inspecting the returned fields