7

我现在正在构建一个基本的时间记录应用程序,并且我有一个使用 django-taggit 的 todo 模型。我的 Todo 模型如下所示:

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

我正在尝试获取项目中所有 Todos 的唯一标签列表,并且我已经设法使用集合理解使其工作,但是对于项目中的每个 Todo,我必须查询数据库以获取标签。我的理解是:

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().

我确实看到有人在这里问了一个非常相似的问题:Optimize django query to pull foreign key and django-taggit relationship但它看起来并没有得到明确的答案。我希望有人可以帮助我。谢谢!

4

4 回答 4

7

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

此拉取请求中引入了此功能。在它的测试用例中,请注意在使用 预取标签后.prefetch_related('tags'),还有 0 个额外的查询用于列出标签。

于 2014-02-10T06:56:28.043 回答
3

有点骇人听闻的解决方案:

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,因为我们必须使用 taggit 在内部使用的 TaggedItem 和 ContentType。

Taggit 没有为您的特定用例提供任何方法。原因是因为它是通用的。taggit 的目的是可以标记任何模型的任何实例。因此,它为此使用了 ContentType 和 GenericForeignKey。

taggit 内部使用的模型是 Tag 和 TaggedItem。模型标签只包含标签的字符串表示。TaggedItem 是用于将这些标签与任何对象相关联的模型。由于标签应该与任何对象相关联,因此 TaggedItem 使用模型 ContentType。

taggit 提供的 api 如tags.all()tags.add()等在内部使用 TaggedItem 和此模型上的过滤器为您提供特定实例的标签。

因为,您的要求是获取特定对象列表的所有标签,我们必须使用 taggit 使用的内部类。

于 2013-03-27T10:21:16.390 回答
1

使用django-tagging和方法 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.
    """
于 2013-08-27T08:31:52.710 回答
0

比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() }
于 2013-10-03T15:44:38.613 回答