Django的taggit prefetch_related(Django-taggit prefe

2019-08-01 13:03发布

现在我建立一个基本的时间记录应用程序,我有一个使用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关系但它看起来并不像它曾经有一个明确的答案。 我希望有人能帮助我。 谢谢!

Answer 1:

Taggit现在支持prefetch_related直接在标签字段(0.11.0版及更高版本,发布2013年11月25日)。

此功能在推出这个pull请求 。 在测试情况下它 ,请注意使用预取标签后.prefetch_related('tags')有0上市标签附加查询。



Answer 2:

轻微的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使用的内部类对象的特定列表。



Answer 3:

使用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.
    """


Answer 4:

稍微少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() }


文章来源: Django-taggit prefetch_related