[Fixed]-How to Serialize generic foreign key In DRF


This case is perfectly described in the documentation.

So if you want to serialize AddressType you will need to implement something like this:

class ContentObjectRelatedField(serializers.RelatedField):
    A custom field to use for the `content_object` generic relationship.

    def to_representation(self, value):
        Serialize tagged objects to a simple textual representation.
        if isinstance(value, Bookmark):
            return 'Bookmark: ' + value.url
        elif isinstance(value, Note):
            return 'Note: ' + value.text
        raise Exception('Unexpected type of tagged object')

Where Bookmark and Note are objects which may be have associated contents.

If you want to serialize AddressBook you can try doing something like this:

class AddressBookSerializer(serializers.ModelSerializer):
    address_object_type = ContentObjectRelatedField()
    class Meta:
        model = AddressBook
        fields = ('id','uuid','address_tag','address_object_type','address1','address2')


I’d prefer use this third party (rest-framework-generic-relations) as also described in documentation.

from generic_relations.relations import GenericRelatedField

class TagSerializer(serializers.ModelSerializer):
    A `TaggedItem` serializer with a `GenericRelatedField` mapping all possible
    models to their respective serializers.
    tagged_object = GenericRelatedField({
        Bookmark: BookmarkSerializer(),
        Note: NoteSerializer()

    class Meta:
        model = TaggedItem
        fields = ('tag_name', 'tagged_object')


If your code is more structured, i.e. each app has its serializers.py, and serializer naming follows a convention (i.e. [ModelName]Serializer) you could use dynamic importing to avoid if ... elif ... else ... logic with a benefit of loose coupling:

from django.utils.module_loading import import_string

class ContentObjectRelatedField(serializers.RelatedField):
    A custom field to serialize generic relations

    def to_representation(self, object):
        object_app = object._meta.app_label
        object_name = object._meta.object_name
        serializer_module_path = f'{object_app}.serializers.{object_name}Serializer'
        serializer_class = import_string(serializer_module_path)
        return serializer_class(object).data

Leave a comment