Django annoyances: no reverse select_related

Consider

for page in Page.objects.all():
  print page.title
  for comment in page.comments.all():
    print comment
.

There will be a single query to fetch all pages, but there will be for every page another query to fetch its comments. Luckily, Django has got a nice trick up its sleave: select_related. Would I use instead of Page.objects.all(), Page.objects.select_related('comments').all() then Django will use a single joined query to prefetch comments for each page.

However, Django’s select_related only supports forward one-to-many references. No many-to-many; certainly no reverce many-to-many; no reverse one-to-many and no, not even reverse one-to-one (yet). A developer claims it’s impossible (which is bullshit), another asks for patches, which means he doesn’t care doing it himself.

It’s quite easy to manually code around the missing reverse select_related, but it takes too many ugly lines compared to the single word it could’ve been.

3 thoughts on “Django annoyances: no reverse select_related

  1. Its something thats been bugging me for quite some time, and just lately its become quite a nusance. If django gracefully degraded and gave you appropriate manual controls for this exact sort of thing it wouldn’t be so bad. But I can’t seem to work that bit out quite yet.

  2. You can work around it using select_related to retrieve comments to the pages, and then combine them in the python code using groupby.

    from django.utils.itercompat import groupby

    comments = Comment.objects.filter(**kwargs).select_related(‘page’)
    pages_with_comments = dict(groupby(comments, lambda t: t.page))

  3. Yeah, that is what I was thinking about. However, it’s still a waste to have to fetch all comments, when possibly only displaying 30 of them. (You changed the direction of the reference in your code).

Leave a Reply

Your email address will not be published. Required fields are marked *