Found misprint?

Select it with the mouse and hit Enter

Ctrl-Enter
Processed:
38 1 199 25
The full repository of DjangoBook translation you can get on GitHub.
We appreciate your patches!
We are glad to hear your questions, comments or suggestions!
(Open in new tab)
Users number: 638
Русская группа
на Google
на поддержку перевода
Яндекс Яндекс.Деньги Хочу такую же кнопку
Ускорить процесс перевода!

ЯМ:41001223475816

Подгрузка связанных моделей (многие-ко-многим)

Django ORM

(c) 2009 alerion.um с гмыла

Select_related не работает для обратных связей. В мануале Django, в примере, используется обычный менеджер для обратных связаных таблиц, который посылает запрос при обращении к ним. Можно, конечно, использовать raw sql, когда нет доступа к методам моделей, например, get_absolute_url и др. Функция load_related_m2m для обьектов в object_list получает все связаные(m2m) через поле field обьекты и заносит в поле "all_" % field обьекта. В результате получаем всего один лишний запрос на каждую связаную таблицу.

Файл models.py

class Post(models.Model):
    name = models.CharField(max_length=255)
    tags = models.ManyToManyField('Tag', blank=True)
    genres = models.ManyToManyField('Genre', blank=True)

class Tag(models.Model):
    name = models.CharField(max_length=255, unique=True)

class Genre(models.Model):
    name = models.CharField(max_length=255, unique=True)

Файл views.py

def test(request):
    from lib import load_related_m2m
    from app.models import Post
    p = Post.objects.all()[:10]
    load_related_m2m(p, 'tags')
    load_related_m2m(p, 'genres')
    return {'posts': p}

Файл test.html

      {% for item in posts %}
            <h3>{{ item.name }}</h3><br/>
            Теги:
            {% for tag in item.all_tags %}
                {{ tag.name }}, 
            {% endfor %}<br/>
            Жанры:
            {% for g in item.all_genres %}
                {{ g.name }},
            {% endfor %}<br/><br/>
      {% endfor %}

Файл lib.py

from django.db.models.sql.constants import LOOKUP_SEP
from django.db.models import sql
from django.db import connection

def load_related_m2m(object_list, field):

    select_fields = ['pk']
    related_field = object_list.model._meta.get_field(field)
    related_model = related_field.rel.to
    cache_name = 'all_%s' % field

    for f in related_model._meta.local_fields:
        select_fields.append('%s%s%s' % (field, LOOKUP_SEP, f.column))

    query = sql.Query(object_list.model, connection)
    query.add_fields(select_fields)
    query.add_filter(('pk__in', [obj.pk for obj in object_list]))

    related_dict = {}
    for row in query.results_iter():
        if row[2]:
            related_dict.setdefault(row[0], []).append(related_model(*row[1:]))

    for obj in object_list:
        try:
            setattr(obj, cache_name, related_dict[obj.pk])
        except KeyError:
            setattr(obj, cache_name, [])

    return object_list


Добавлено 20 июля 2010

Для Django 1.2 данный код был доработан Анатолием Лариным. Django 1.2.1 и load_related_m2m

alerion
alerion 11 months, 2 weeks ago
Answer | Link

А пригодился все таки :)