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