如何在django taggit中使用django的Q?

时间:2013-07-02 22:40:09

标签: django django-taggit django-q

我有一个用&#34标记的Result对象;一个"和"两个"。当我尝试查询标记为"一个" "两个",我什么也得不回来:

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

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

5 个答案:

答案 0 :(得分:3)

首先,这三个是相同的:

(index, i+1)

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

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

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"])

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

答案 1 :(得分:3)

django-taggit实现标记的方法实质上是通过ManytoMany关系实现的。在这种情况下,数据库中存在一个单独的表来保存这些关系。由于将两个模型连接在一起,因此通常称为“直通”或中间模型。在django-taggit的情况下,这称为TaggedItem。因此,您有一个Result模型即您的模型,并且有django-taggit提供的两个模型TagTaggedItem

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

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

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

要解决此问题,您可以:

选项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个标签,它都是完全相同的查询。

请查看此内容,并让我知道是否需要澄清。

答案 2 :(得分:0)

进入可行的示例,您可以在两个python对象(查询集)上结束。这适用于任何记录,不一定是onetwo作为标记的同一记录。

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

答案 3 :(得分:0)

q = Result.objects.filter(tags_ name _in = [“one”])。filter(tags_ name _in = [“two”])

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

答案 4 :(得分:0)

也许intersectionQ()有用:

tags_list = ["one", "two"]
Result.objects.intersection(*[Result.objects.filter(tags__name=tag) for tag in tags_list])