我有一个todo模型定义如下:
class Action(models.Model):
name = models.CharField("Action Name", max_length=200, unique = True)
complete = models.BooleanField(default=False, verbose_name="Complete?")
reoccurance = models.ForeignKey(Reoccurance, blank=True, null=True, verbose_name="Reoccurance")
notes = models.TextField("Notes", blank=True)
tags = TaggableManager()
class Reoccurance(models.Model):
label = models.CharField("Label", max_length=50, unique = True)
days = models.IntegerField("Days")
我想列出所有不完整的操作:
actions = Action.objects.filter(complete=False)
动作列表的模板循环:
{% for action in actions %}
<p>{{ action }}</p>
{% if action.reoccurance %}
<p>{{ action.reoccurance }}</p>
{% endif %}
{% for tag in action.tags.all %}
<span>{{ tag }}</span>{% if not forloop.last %}, {% endif %}
{% endfor %}
{% endfor %}
使用django-debug-toolbar,我发现对于每个操作,我都在{%if action.reoccurance%}和{%for action in action.tags.all%}中访问数据库。
有没有更好的方法来编写我的查询,以便数据库不会针对循环的每次迭代进行ping操作?我认为它与select_related有关,但我不知道如何处理django-taggit。
更新我收到了部分答案。 select_related确实有效,但我必须指定reoccurance,可能是因为我不能将它用于标签:
actions = Action.objects.select_related('reoccurance').filter(complete=False)
问题仍然存在,我在模板循环中为每个“action.tags.all”命中数据库。是否可以在django-taggit上使用某种预取?
答案 0 :(得分:1)
可以使用prefetch_related
来检索标记,但是你需要绕过'tags'属性,因为 - 正如jdi所说 - 这是一个自定义管理器而不是真正的关系。相反,你可以这样做:
actions = Action.objects.select_related('reoccurance').filter(complete=False)\
.prefetch_related('tagged_items__tag')
不幸的是,您的模板代码中的action.tags.all
将不会使用预取,并最终会执行自己的查询 - 因此您需要采取相当愚蠢的步骤绕过“标记”管理器:
{% for tagged_item in action.tagged_items.all %}
<span>{{ tagged_item.tag }}</span>{% if not forloop.last %}, {% endif %}
{% endfor %}
(编辑:如果你得到“'QuerySet'对象没有属性'prefetch_related'”,这表明你的Django版本低于1.4,其中prefetch_related不可用。)
答案 1 :(得分:-1)
问题是tags
不是一个字段,而是一个自定义管理器,它位于类级别,只是执行查询。
我不确定这是否适用于自定义管理器,因为它适用于生成类似查询集的多对多字段等。但是如果你使用的是django 1.4,你可以试试prefetch_related
。它将再做一个查询来批量处理关系并缓存它们。
再次免责声明:我不知道这是否适用于经理
actions = Action.objects.select_related('reoccurance').filter(complete=False)\
.prefetch_related('tags')