[Django]-Django: backward ForeignKey queries

2👍

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 %}

2👍

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

-1👍

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.

Leave a comment