现在我建立一个基本的时间记录应用程序,我有一个使用Django的taggit待办事项模型。 我的待办事项模式是这样的:
class Todo(models.Model):
project = models.ForeignKey(Project)
description = models.CharField(max_length=300)
is_done = models.BooleanField(default=False)
billable = models.BooleanField(default=True)
date_completed = models.DateTimeField(blank=True, null=True)
completed_by = models.ForeignKey(User, blank=True, null=True)
tags = TaggableManager()
def __unicode__(self):
return self.description
我试图让独特的标签的列表中的所有的托多斯一个项目,我已经成功地得到这个使用一套理解的工作,但是,对于每一个待办事项的项目,我要查询数据库来获取标签。 我的理解组是:
unique_tags = { tag.name.lower() for todo in project.todo_set.all() for tag in todo.tags.all() }
这一切正常,但在项目运行一个单独的查询来获取所有标记每个待办事项。 我想知道是否有任何方法可以让我做类似的事情,以避免这些重复的查询prefetch_related:
unique_tags = { tag.name.lower() for todo in project.todo_set.all().prefetch_related('tags') for tag in todo.tags.all() }
运行上面的代码给我的错误:
'tags' does not resolve to a item that supports prefetching - this is an invalid parameter to prefetch_related().
我没看到有人问了一个非常类似的问题在这里: 优化Django的查询拉外键和Django的taggit关系但它看起来并不像它曾经有一个明确的答案。 我希望有人能帮助我。 谢谢!
Taggit现在支持prefetch_related
直接在标签字段(0.11.0版及更高版本,发布2013年11月25日)。
此功能在推出这个pull请求 。 在测试情况下它 ,请注意使用预取标签后.prefetch_related('tags')
有0上市标签附加查询。
轻微的hackish soution:
ct = ContentType.objects.get_for_model(Todo)
todo_pks = [each.pk for each in project.todo_set.all()]
tagged_items = TaggedItem.objects.filter(content_type=ct, object_id__in=todo_pks) #only one db query
unique_tags = set([each.tag for each in tagged_items])
说明
我说这是因为hackish的我们不得不使用TaggedItem和ContentType的这taggit内部使用。
Taggit不提供特定的使用情况下,任何方法。 究其原因是因为它是通用的。 对于taggit的意图是,任何模型的任何实例可以被标记。 因此,它使用的ContentType和GenericForeignKey的了点。
在taggit内部使用的模型是标签和TaggedItem。 型号标记只包含标签的字符串表示。 TaggedItem是用来把这些标签与任何对象关联的模型。 由于标签应地相联与任何对象,TaggedItem使用模型的ContentType。
通过taggit像tags.all提供的API(),tags.add()等内部就这一模型使用TaggedItem和过滤器的给你特定实例的标签。
因为,你的要求是让所有的标签,我们不得不使用由taggit使用的内部类对象的特定列表。
使用Django的标记和方法usage_for_model
def usage_for_model(self, model, counts=False, min_count=None, filters=None):
"""
Obtain a list of tags associated with instances of the given
Model class.
If ``counts`` is True, a ``count`` attribute will be added to
each tag, indicating how many times it has been used against
the Model class in question.
If ``min_count`` is given, only tags which have a ``count``
greater than or equal to ``min_count`` will be returned.
Passing a value for ``min_count`` implies ``counts=True``.
To limit the tags (and counts, if specified) returned to those
used by a subset of the Model's instances, pass a dictionary
of field lookups to be applied to the given Model as the
``filters`` argument.
"""
稍微少hackish的答案比AKSHAR的,但幅度不大...
正如你穿越tagged_item关系,使用条款,你可以只要使用prefetch_related prefetch_related('tagged_items__tag')
不幸的是, todo.tags.all()
不会采取优势在于预取的- '标记的经理仍然会最终会做自己的查询-所以你必须有步骤在tagged_items关系了。 这应该做的工作:
unique_tags = { tagged_item.tag.name.lower()
for todo in project.todo_set.all().prefetch_related('tagged_items__tag')
for tagged_item in todo.tagged_items.all() }