(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