Use of select_related in nested serializers in rest framework

I am trying to get my head around some aspects of DRF and am struggling with nested serializers.

Say I’m using the standard blog/posts example, like this:-

class Post(models.Model):
    title = models.CharField()
    content = models.TextField()
    author = models.ForeignKey(User)


class Comment(models.Model):
    post = models.ForeignKey(Post)
    content = models.TextField()

If I want to serialize the Post (with depth=1) for every Comment when I’m fetching all of the Comments (/api/comments/) then I will need to change the queryset in the APIView to be:-

Comment.objects.select_related('post__author')

as it will need to fetch all of the Posts AND each Post’s author to ensure there aren’t too many queries.

But that implies that the Comments APIView has internal knowledge of what the Posts look like. If the Post object changes, I’ll have to somehow “know” to update the Comment queryset to fetch the new stuff. This could get messy and there could be loads of things that serialize Posts in this way and there’s no realistic way I can know to update them all.

Is there a better way of doing this?

1 Like

I’m definitely intrigued by this question. As far as I know there isn’t a quick and easy way to accomplish it. The way I would approach it would be one of the following (numbered to make talking about specific options easier):

  1. There is a ticket about not being able to slice a Prefetch object. However, there’s a workaround in the comments.
  2. Rework the UX to load the posts in one ajax call, then load the comments using the ids. It simplifies each API endpoint, but will cause the full rendered time to take longer. On the other hand, it could get the first information rendered faster.
  3. Rework the view to return two sets of collections of data. From a high level you could create the posts data set, then fetch the comments from the DB and formulate that into a data set. Return in a {"posts": [], "comments": []} structure.
  4. If using postgres and can wait for Django 4, use ArraySubquery
  5. Rework the view to generate a list of Post, then pass that into the serializer. The queryset will still use Comment, but your serializers would be saved from the complexities of how to generate the queryset in the most efficient manner.

Personally, I prefer to keep the code as clean and as readable as possible. So for that reason, I think number 1 makes the most sense - you request the exact things you need.

But then again, I’m not sure it makes much sense to get a list of Comments without any indication of which Post they belong to so the /comments endpoint seems a bit pointless then.

In my mind it wouldn’t have a /comments endpoint. It’d be more like /initial-comments that would require a querystring parameter post_ids[]= or a list of post ids in the body of the request.