我有一个跟踪出版物的Django应用程序。出版物与作者有M2M关系。使用MySQL。
简单。
class Publication(models.Model):
slug = models.SlugField(unique=True, max_length=128)
author = models.ManyToManyField(Author, blank=True, null=True, through='Authorship')
title = models.CharField(max_length=128)
def __unicode__(self):
return unicode(self.title)
我有一个ListView
来展示他们:
class PubList(ListView):
model = Publication
其中大多数是研究论文,有几位作者。在我的模板上,我想显示作者列表。所以我做了这样的事情:
{% for obj in publication_list %}
<tr>
<td><a href="{{ obj.get_absolute_url }}">{{ obj.title }}</a></td>
<td>
{% for a in obj.authorship_set.all %}
{{ a.author.last_name }}, {{ a.author.first_name }}
{% if not forloop.last %}; {% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
好吧,你可能会猜到我的问题是什么。随着Publications
的数量增加,数据库调用量猛增。 119种出版物有500多种查询。
我这样解决了:
在我的PubList(ListView)
中,我覆盖get_context_data
并将此函数的输出设置为上下文['authors']:
def get_authors_by_pub():
from django.db import connection
sql = """SELECT p.id,
(
SELECT GROUP_CONCAT(CONCAT(a.last_name, ', ', a.first_name) SEPARATOR '; ')
FROM publication_authorship ap
LEFT JOIN publication_author a ON a.id = ap.author_id
WHERE ap.publication_id = p.id
)
FROM publication_publication p"""
cursor = connection.cursor()
cursor.execute(sql)
rows = cursor.fetchall() or ()
authors = {}
for r in rows:
if r[1]:
authors[r[0]] = r[1]
return authors
现在我有一个作者词典,如:
{1: 'Tesla, Nikola; Clarke, Aurthur; Hooper, Grace', 2: 'Hopper, Grace; Simpson, Marge'}
然后,在模板上,由于我无法按密钥访问字典,因此我遍历authors
以找到密钥为publication.id
的字典:
<td>
{% for key, value in authors.items %}
{% if key == obj.id %}
{{ value }}
{% endif %}
{% endfor %}
</td>
这个工作,只有2个查询。即使作者查询是残酷的,使用嵌套的SELECT,它也比以前快了几个数量级。
但我想知道是否有更好的方法。对于模板上的每个出版物,我觉得有点狡猾地遍历整个dict。我希望能够在模板上authors[obj.id]
。
您怎么看?
答案 0 :(得分:1)
Django在其文档中详细介绍了相关查询和延迟加载...为什么你会在django提供时编写所有这些代码:
Publication.objects.prefetch_related('authors').all()
https://docs.djangoproject.com/en/1.6/topics/db/queries/#related-objects https://docs.djangoproject.com/en/1.6/ref/models/querysets/#prefetch-related
您可以在ListView中使用上述查询集:
class PublList(ListView):
queryset = Publication.objects.prefetch_related('authors')