如何将django的Q与django taggit一起使用? [英] How do I use django's Q with django taggit?

查看:59
本文介绍了如何将django的Q与django taggit一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个标记为一个"和两个"的Result对象.当我尝试查询标记为一个" 两个"的对象时,我什么也没回来:

I have a Result object that is tagged with "one" and "two". When I try to query for objects tagged "one" and "two", I get nothing back:

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

为什么Q不起作用?我该如何运作?

Why does it not work with Q? How can I make it work?

推荐答案

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

The way django-taggit implements tagging is essentially through a ManytoMany relationship. In such cases there is a separate table in the database that holds these relations. It is usually called a "through" or intermediate model as it connects the two models. In the case of django-taggit this is called TaggedItem. So you have the Result model which is your model and you have two models Tag and TaggedItem provided by django-taggit.

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

When you make a query such as Result.objects.filter(Q(tags__name="one")) it translates to looking up rows in the Result table that have a corresponding row in the TaggedItem table that has a corresponding row in the Tag table that has the name="one".

尝试匹配两个标签名称将转换为在Result表中查找在TaggedItem表中具有对应行的行,在TaggedItem表中具有对应行,而在Tag表中具有对应行,并且两个名称都为"one".AND名称=两个".显然,您永远都不会拥有它,因为您仅连续拥有一个值,因此它要么是一个",要么是一个值.或两个".

Trying to match for two tag names would translate to looking up up rows in the Result table that have a corresponding row in the TaggedItem table that has a corresponding row in the Tag table that has both name="one" AND name="two". You obviously never have that as you only have one value in a row, it's either "one" or "two".

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

These details are hidden away from you in the django-taggit implementation, but this is what happens whenever you have a ManytoMany relationship between objects.

要解决此问题,您可以:

To resolve this you can:

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

Query tag after tag evaluating the results each time, as it is suggested in the answers from others. This might be okay for two tags, but will not be good when you need to look for objects that have 10 tags set on them. Here would be one way to do this that would result in two queries and get you the result:

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

您可以通过一个查询来实现此目的,因此从应用程序到数据库只有一趟路程,如下所示:

You could achieve this with a single query so you only have one trip from the app to the database, which would look like this:

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

This would only make one trip to the database. (Note: filtering on sub-queries requires django 3.0).

但是您仍然仅限于两个标签.如果您需要检查10个或更多标签,则上述方法实际上是行不通的...

But you are still limited to two tags. If you need to check for 10 tags or more, the above is not really workable...

直接查询关系表并以给您对象ID的方式汇总结果.

Query the relationship table instead directly and aggregate the results in a way that give you the object IDs.

#  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中包含您要查找的标签数量的对象.

TaggedItem is the hidden table in the django-taggit implementation that contains the relationships. The above will query that table and aggregate all the rows that refer either to the "one" or "two" tags, group the results by the ID of the objects and then pick those where the object ID had the number of tags you are looking for.

这是一个查询,最后将为您提供已被两个标签标记的所有对象的ID.无论您是否需要2个标签或200个标签,它也是完全相同的查询.

This is a single query and at the end gets you the IDs of all the objects that have been tagged with both tags. It is also the exact same query regardless if you need 2 tags or 200.

请对此进行检查,并让我知道是否需要澄清.

Please review this and let me know if anything needs clarification.

这篇关于如何将django的Q与django taggit一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆