【问题标题】:How do I use django's Q with django taggit?如何将 django 的 Q 与 django taggit 一起使用?
【发布时间】:2013-06-30 11:49:56
【问题描述】:

我有一个标有“一”和“二”的 Result 对象。当我尝试查询标记为“一”“二”的对象时,我什么也得不到:

q = Result.objects.filter(Q(tags__name="one") & Q(tags__name="two"))
print len(q) 
# prints zero, was expecting 1

为什么它不适用于 Q?我怎样才能让它发挥作用?

【问题讨论】:

    标签: django django-taggit django-q


    【解决方案1】:

    在示例中,您对两个 python 对象(查询集)进行了结束。这适用于任何记录,不一定适用于具有 onetwo 作为标签的同一记录。

    ps:为什么要使用in 过滤器?

    【讨论】:

    • 谢谢。我已经编辑了我的问题以消除干扰和 __in 过滤器。
    【解决方案2】:

    q = Result.objects.filter(tags_name_in=["one"]).filter(tags_name_in=["two"])

    添加 .distinct() 如果需要多个唯一对象,则删除重复项

    【讨论】:

      【解决方案3】:

      首先,这三个是一样的:

      Result.objects.filter(tags__name="one", tags__name="two")
      Result.objects.filter(Q(tags__name="one") & Q(tags__name="two"))
      Result.objects.filter(tags__name_in=["one"]).filter(tags__name_in=["two"])
      

      我认为名称字段是 CharField,没有记录可以同时等于“一”和“二”。

      在 python 代码中,查询看起来像这样(总是错误,为什么你没有得到结果):

      from random import choice
      
      name = choice(["abtin", "shino"])
      
      if name == "abtin" and name == "shino":
      

      我们使用 Q 对象来实现 OR 或复杂查询

      【讨论】:

      • 在最近的 Django 版本中,第一个查询集抛出 SyntaxError: keyword argument repeated,第二个给出空结果,第三个确实有效,但需要链接查询集的次数未知。第三个已经在另一个答案中提到,__in 不是必需的。使用AND逻辑和Taggit通过多个标签过滤查询集的解决方案是什么?
      • 不知道 SyntaxError,很酷。实际上抛出错误是合乎逻辑的。为什么有人应该做一个总是返回 None 的查询?
      【解决方案4】:

      django-taggit 实现标记的方式本质上是通过多对多关系。在这种情况下,数据库中有一个单独的表来保存这些关系。它通常被称为“直通”或中间模型,因为它连接了两个模型。在 django-taggit 的情况下,这称为TaggedItem。所以你有 Result 模型这是你的模型,你有两个模型 TagTaggedItem 由 django-taggit 提供。

      当您进行诸如Result.objects.filter(Q(tags__name="one")) 之类的查询时,它会转换为在 Result 表中查找在 TaggedItem 表中具有对应行的行,而在 Tag 表中具有 name="one" 的对应行。

      尝试匹配两个标签名称将转化为在 Result 表中查找在 TaggedItem 表中具有对应行的行,在 Tag 表中具有 name="one" 和 name= 的对应行“二”。您显然永远不会拥有它,因为您连续只有一个值,它要么是“一”,要么是“二”。

      这些细节在 django-taggit 实现中是隐藏的,但只要对象之间存在多对多关系,就会发生这种情况。

      要解决此问题,您可以:

      选项 1

      每次评估结果的标签后查询标签,正如其他人的答案中所建议的那样。这对于两个标签可能没问题,但当您需要查找设置了 10 个标签的对象时就不好了。这是执行此操作的一种方法,它会导致两个查询并为您提供结果:

      # get the IDs of the Result objects tagged with "one"
      query_1 = Result.objects.filter(tags__name="one").values('id')
      # use this in a second query to filter the ID and look for the second tag.
      results = Result.objects.filter(pk__in=query_1, tags__name="two")
      

      您可以通过单个查询来实现这一点,因此您只需从应用程序到数据库的一次行程,如下所示:

      # create django subquery - this is not evaluated, but used to construct the final query
      subquery = Result.objects.filter(pk=OuterRef('pk'), tags__name="one").values('id')
      # perform a combined query using a subquery against the database
      results = Result.objects.filter(Exists(subquery), tags__name="two")
      

      这只会访问一次数据库。 (注意:过滤子查询需要 django 3.0)。

      但您仍然只能使用两个标签。如果需要检查10个或更多标签,上面的确实不可行...

      选项 2

      改为直接查询关系表并以提供对象 ID 的方式聚合结果。

      #  django-taggit uses Content Types so we need to pick up the content type from cache
      result_content_type = ContentType.objects.get_for_model(Result)
      tag_names = ["one", "two"]
      tagged_results = (
          TaggedItem.objects.filter(tag__name__in=tag_names, content_type=result_content_type)
              .values('object_id')
              .annotate(occurence=Count('object_id'))
              .filter(occurence=len(tag_names))
              .values_list('object_id', flat=True)
      )
      

      TaggedItem 是 django-taggit 实现中包含关系的隐藏表。上面将查询该表并聚合所有引用“一”或“二”标签的行,按对象的 ID 对结果进行分组,然后选择对象 ID 具有您正在查找的标签数量的那些为。

      这是一个单独的查询,最后会为您提供所有已使用两个标签标记的对象的 ID。无论您需要 2 个标签还是 200 个标签,这也是完全相同的查询。

      请查看此内容,如果有任何需要澄清的地方,请告诉我。

      【讨论】:

      • 这应该在他们的文档中,因为我想用 AND 而不是 OR 搜索多个标签会经常使用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-01
      • 1970-01-01
      • 2011-09-19
      • 2019-09-04
      • 2019-11-13
      • 2017-12-25
      • 2019-03-12
      相关资源
      最近更新 更多