Django: backward ForeignKey queries
My models:
class NewsItem(models.Model):
title = ...
content = ...
class Image(models.Model):
newsItem = models.ForeignKey(NewsItem)
url = ....
I want to display 50 NewsItems with their images ( 2-5 images for each NewsItem ). Can i do it with only one or two queries? Can i query "backwars" related information?
I found some information about it, but didn't unders开发者_开发问答tand how to show _related items in template (in my case "backward" _related items are Images). The link is http://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/
Article was posted on Jan 2010 - may be there is more efficient way?
Here's an example that might be easier to follow (naming).
Thank you to Daniel Roseman for setdefault
! I learn something on stack every day. I've been using ugly try/except
blocks to solve this same issue.
View:
newsitems = NewsItem.objects.all()[0:50]
related_images = Image.objects.filter(newsitem__in=newsitems)
newsitem_images_map = {}
for image in related_images:
# start appending to a list keyed by the newsitem ID for all related images
newsitem_images_map.setdefault(image.newsitem_id, []).append(image)
for newsitem in newsitems:
# set an attribute on the newsitem that is the list created above
newsitem.images = newsitem_images_map.get(newsitem.id)
# this attribute is accessible from the template.
Template:
{% for newsitem in newsitems %}
{{ newsitem.title }}
{% for image in newsitem.images %}
{{ image }}
{% endfor %}
{% endfor %}
qs = NewsItem.objects.all()
obj_dict = dict([(obj.id, obj) for obj in qs])
objects = Image.objects.filter(newsitem__in=qs)
relation_dict = {}
for obj in objects:
relation_dict.setdefault(obj.newsitem_id, []).append(obj)
for id, related_items in relation_dict.items():
obj_dict[id]._related_images = related_items
...
{% for newsitem in obj_dict %}
{% for image in newsitem._related_images %}
You will get your related data in 2 queries
UPDATE: In Django 1.4 available new method prefetch_related
Btw, prefetch_related doesn't work with deprecated generic view direct_to_template.
newitems = NewItem.objects.prefetch_related('images')
You scenario is NOT a reverse relation:
news_item.images # forward relation
image.newsitem_set # reverse relation
So, all you need to do is:
news_items = NewsItem.objects.select_related().all()[:50]
and pass it to your template, image objects should already be cached.
精彩评论