Serializing a generic relation in django-rest-fram

2019-03-31 05:51发布

问题:

I am trying to learn how to use GenericRelations in Django-Rest-Framework. I found the documetation page for serializer relations and followed the codes. I created the models:

class TaggedItem(models.Model):
    """
    Tags arbitary model instance using a generic relation.

    """
    tag_name = models.SlugField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    tagged_object = GenericForeignKey('content_type', 'object_id')

    def __unicode__(self):
        return self.tag_name

class Bookmark(models.Model):
    """
    A bookmark consists of a URL, and 0 or more descriptive tags.
    """
    link_url = models.URLField()
    tags = GenericRelation(TaggedItem)

class Note(models.Model):
    """
    A note consists of some texts, and 0 or more descriptive tags
    """
    text = models.CharField(max_length=1000)
    tags = GenericRelation(TaggedItem)

I created the serializers:

class BookmarkSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bookmark
        fields = ('url', 'link_url', )


class NoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Note
        fields = ('url', 'text', )


class TaggedObjectRelatedField(serializers.RelatedField):
    """
    A custom field to use for the 'tagged_object' generic relationship
    """

    def to_representation(self, value):
        """
        Serialize tagged objects to their respective serializer formats
        :param value:
        :return:
            serializer.data
        """
        if isinstance(value, Bookmark):
            return 'Bookmark: ' + value.url
        elif isinstance(value, Note):
            return 'Note: ' + value.text
        raise Exception('Unexpected type of tagged object')


class TaggedItemSerializer(serializers.ModelSerializer):
    tagged_object = TaggedObjectRelatedField()
    class Meta:
        model = TaggedItem
        fields = ('url', 'id', 'tag_name', 'tagged_object')

Now what should be the input to the TaggedObjectRelatedField() inside TaggedItemSerializer? Currently I am getting error as

  File "/home/aswin/Documents/WebProjects/drf_practice/uni_auth//loginpage/login/serializers.py", line 76, in TaggedItemSerializer
    tagged_object = TaggedObjectRelatedField()
  File "/home/aswin/Documents/WebProjects/drf_practice/uni_auth/lib/python3.5/site-packages/rest_framework/relations.py", line 80, in __init__
    'Relational field must provide a `queryset` argument, '
AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`.

I tried giving the arguments as read_only=True too but it threw the same error.just to put what i exactly did:

class TaggedItemSerializer(serializers.ModelSerializer):
    tagged_object = TaggedObjectRelatedField(read_only=True)

Please help me with this

回答1:

I am not seeing anything in the article you refer to about serializing the TaggedItem, why do you want to serialize the TaggedItem?

Here is my serializer class:

from models import Bookmark, Note, TaggedItem
from rest_framework import serializers


class TaggedObjectRelatedField(serializers.RelatedField):
    def to_representation(self, value):
        if isinstance(value, Bookmark):
            serializer = BookMarkSerializer(value)
        elif isinstance(value, Note):
            serializer = NoteSerializer(value)
        else:
            raise Exception('Unexpected type of tagged object')

        return serializer.data


class BookMarkSerializer(serializers.HyperlinkedModelSerializer):
    tags = TaggedObjectRelatedField(many=True, queryset=TaggedItem.objects.all())

    class Meta:
        model = Bookmark
        fields = ('pk', 'url', 'tags')


class NoteSerializer(serializers.HyperlinkedModelSerializer):
    tags = TaggedObjectRelatedField(many=True, queryset=TaggedItem.objects.all())

    class Meta:
        model = Note
        fields = ('pk', 'text', 'tags')


回答2:

A bit late to the party but I think if you would like to create a serializer for TaggedItem you may checkout rest-framework-generic-relations. So according to their documentation, you could create something like:

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

or something like:

class TagSerializer(serializers.ModelSerializer):
    """
    A `Tag` serializer with a `GenericRelatedField` mapping all possible
    models to properly set up `HyperlinkedRelatedField`s.
    """
    tagged_object = GenericRelatedField({
        Bookmark: serializers.HyperlinkedRelatedField(
            queryset = Bookmark.objects.all(),
            view_name='bookmark-detail',
        ),
        Note: serializers.HyperlinkedRelatedField(
            queryset = Note.objects.all(),
            view_name='note-detail',
        ),
    })

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