Naresh
Naresh

Reputation: 1972

How to Serialize generic foreign key In DRF

I have model with generic foreign key and I want to serialize that model.

model.py:

class AddressType(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type','object_id')

    def __unicode__(self):
        return u'%s' % str(self.content_type)

class AddressBook(TimeStampedModel):
    class Meta:
        db_table = 'address_book'

    uuid = UUIDField(auto=True)
    address_tag = models.CharField(null=True, blank=True, max_length=20)

    # should be a generic foreign key
    address_object_type = GenericRelation(AddressType)
    address1 = models.CharField(
        verbose_name='Address1',
        max_length=200,
    )
    address2 = models.CharField(
        verbose_name='Address2',
        max_length=200,
    )

serializer.py:

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

How can I serialize JSON on above model?

Upvotes: 12

Views: 10988

Answers (3)

pgorecki
pgorecki

Reputation: 699

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

Upvotes: 2

msmaromi
msmaromi

Reputation: 535

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')

Upvotes: 10

DevilPinky
DevilPinky

Reputation: 558

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')

Upvotes: 16

Related Questions