优化django查询以获取外键和django-taggit关系

时间:2012-08-30 19:57:30

标签: mysql django django-templates django-taggit

我有一个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上使用某种预取?

2 个答案:

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