【问题标题】:Django-taggit prefetch_relatedDjango-taggit prefetch_related
【发布时间】:2012-10-07 05:19:51
【问题描述】:

我现在正在构建一个基本的时间记录应用程序,并且我有一个使用 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

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

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 但是它看起来并没有得到明确的答案。我希望有人可以帮助我。谢谢!

【问题讨论】:

  • 我很惊讶这个问题仍然没有答案。你找到了吗?
  • @teewuane:Taggit 最近为此添加了一项功能。

标签: django django-taggit


【解决方案1】:

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

此功能是在this pull request 中引入的。在the test case for it 中,注意在使用.prefetch_related('tags') 预取标签后,还有0 个额外的查询来列出标签。

【讨论】:

    【解决方案2】:

    比akshar 的回答稍微不那么骇人听闻,但只是稍微...

    你可以使用prefetch_related,只要你自己遍历tagged_item关系,使用子句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() }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        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 使用的内部类。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-03-12
          • 2011-07-18
          • 1970-01-01
          • 1970-01-01
          • 2013-09-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多