Search code examples
pythondjangodjango-rest-frameworkmany-to-many

Django REST framework - Getting data instead of links


I have taken a look around, and I didn't find an answer to this question.

Serializers.py

class PostSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Post
        fields = ['title', 'body', 'comments', 'user', 'date']

class CommentSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Comment
        fields = ['body', 'user', 'date']

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'user']

Models.py

class Post(models.Model):
    # Children 
    comments = models.ManyToManyField('Comment', blank=True)

    # Content
    title = models.TextField(default="-->Title here<--")
    body = models.TextField(default="-->Body here<--")

    # About
    user = models.ForeignKey(User) 
    date = models.DateTimeField(auto_now=True)


    object_id = models.PositiveIntegerField(default=0)
    content_type = models.ForeignKey(ContentType, default=0)
    content_object = fields.GenericForeignKey()

    def __str__(self):            
        return str(self.title)


class Comment(models.Model):
    comments = models.ManyToManyField('Comment', blank=True)

    # Content
    body = models.TextField(default="-->Body here<--") 

    # About
    user = models.ForeignKey(User)
    date = models.DateTimeField(auto_now=True)

    object_id = models.PositiveIntegerField(default=0)
    content_type = models.ForeignKey(ContentType, default=0)
    content_object = fields.GenericForeignKey()

    def __str__(self):            
        return str(self.body)

Views.py

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


class CommentViewSet(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer


def index(request):
    return render(request, 'index.html', [])

The index template is a "homepage" that loads the latest posts. Under each post, I'm showing the comments. In the JSON those are links, and I found out how to load them trougth the links (so it's working).

Someone told me that instead of doing it this way, I should make it "load" the comments in the backend, and send them together with the posts (the data, not links). He told me to take a look into: http://www.django-rest-framework.org/api-guide/filtering/#overriding-the-initial-queryset

I can't really figure it out.

How do I get the data, insted of links for the ManyToManyField?


Solution

  • To unfold all the related data one level deep you can use depth param:

    class PostSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = Post
            fields = ['title', 'body', 'comments', 'user', 'date']
            depth = 1
    

    This will replace post.user and post.comments ids with actual records. depth=2 will also unfold post.comments.user. If you want to selectively pull post.comments only one level deep and not post.user:

    class PostSerializer(serializers.HyperlinkedModelSerializer):
        comments = CommentSerializer(many=True) #has to be declared higher above
        class Meta:
            model = Post
            fields = ['title', 'body', 'comments', 'user', 'date']
    

    If on top you want to have post.comments.user unfolded, need to either put depth=1 into existing comment serializer or create a new serializer with unfolded users just for this view, similar to examples above.

    Also make sure you are using prefetch_related on your queryset or the performance will take a serious hit, like:

    Post.objects.all().prefetch_related('comments', 'comments__user')