我有一个用&#34标记的Result对象;一个"和"两个"。当我尝试查询标记为"一个" 和"两个",我什么也得不回来:
q = Result.objects.filter(Q(tags__name="one") & Q(tags__name="two"))
print len(q)
# prints zero, was expecting 1
为什么它不适用于Q?我怎样才能使它发挥作用?
答案 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提供的两个模型Tag
和TaggedItem
。
当您进行诸如Result.objects.filter(Q(tags__name="one"))
之类的查询时,它转换为在Result表中查找行,该行在TaggedItem表中具有对应的行,在Tag表中具有名称=“ one”的对应行“。
尝试匹配两个标记名称将转化为在Result表中查找在TaggedItem表中具有对应行的行,在TaggedItem表中具有对应的行,在Tag表中具有对应的行,同时具有name =“ one” AND name = “二”。显然,您永远都不会拥有它,因为您连续只有一个值,要么是“一个”,要么是“两个”。
在django-taggit实现中,这些细节对您隐藏了,但是只要您在对象之间具有ManytoMany关系,就会发生这种情况。
要解决此问题,您可以:
在每次评估结果之后,都在查询标签之后查询标签,这在其他人的回答中建议。这对于两个标签可能没问题,但是当您需要查找在其上设置了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个或更多标签,则上述方法实际上是不可行的...
直接查询关系表并以给您对象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对象(查询集)上结束。这适用于任何记录,不一定是one
和two
作为标记的同一记录。
ps:为什么使用in
过滤器?
答案 3 :(得分:0)
q = Result.objects.filter(tags_ name _in = [“one”])。filter(tags_ name _in = [“two”])
添加.distinct()以删除重复项,如果需要多个唯一对象
答案 4 :(得分:0)
也许intersection比Q()
有用:
tags_list = ["one", "two"]
Result.objects.intersection(*[Result.objects.filter(tags__name=tag) for tag in tags_list])