Python 如何找到两个 Django 查询集的交集?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4411213/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I find the intersection of two Django querysets?
提问by Paul D. Waite
I've got a Django model with two custom manager methods. Each returns a different subset of the model's objects, based on a different property of the object.
我有一个带有两个自定义管理器方法的 Django 模型。每个都根据对象的不同属性返回模型对象的不同子集。
class FeatureManager(models.Manager):
def without_test_cases(self):
return self.get_query_set().annotate(num_test_cases=models.Count('testcase_set')).filter(num_test_cases=0)
def standardised(self):
return self.get_query_set().annotate(standardised=Count('documentation_set__standard')).filter(standardised__gt=0)
(Both testcase_setand documentation_setrefer to ManyToManyFields on other models.)
(两者testcase_set并documentation_set参考ManyToManyField其他型号秒。)
Is there any way to get a queryset, or just a list of objects, that's the intersectiond of the querysets returned by each manager method?
有没有办法获得一个查询集,或者只是一个对象列表,这是每个管理器方法返回的查询集的交集?
采纳答案by S.Lott
Refactor
重构
class FeatureManager(models.Manager):
@staticmethod
def _test_cases_eq_0( qs ):
return qs.annotate( num_test_cases=models.Count('testcase_set') ).filter(num_test_cases=0)
@staticmethod
def _standardized_gt_0( qs ):
return qs.annotate( standardised=Count('documentation_set__standard') ).filter(standardised__gt=0)
def without_test_cases(self):
return self._test_cases_eq_0( self.get_query_set() )
def standardised(self):
return self._standardized_gt_0( self.get_query_set() )
def intersection( self ):
return self._test_cases_eq_0( self._standardized_gt_0( self.get_query_set() ) )
回答by Spacedman
One way may be to use the python sets module and just do an intersection:
一种方法可能是使用 python sets 模块并只做一个交集:
make a couple of query sets that overlap at id=5:
创建几个在 id=5 处重叠的查询集:
In [42]: first = Location.objects.filter(id__lt=6)
In [43]: last = Location.objects.filter(id__gt=4)
"import sets" first (gets a deprecation warning... ummm... oh well). Now build and intersect them - we get one element in the set:
首先是“导入集”(收到弃用警告……嗯……好吧)。现在构建并交叉它们 - 我们在集合中得到一个元素:
In [44]: sets.Set(first).intersection(sets.Set(last))
Out[44]: Set([<Location: Location object>])
Now get the id of the intersection elements to check it really is 5:
现在获取相交元素的 id 以检查它是否真的是 5:
In [48]: [s.id for s in sets.Set(first).intersection(sets.Set(last))]
Out[48]: [5]
This obviously hits the database twice and returns all the elements of the query set - better way would be to chain the filters on your managers and that should be able to do it in one DB hit and at the SQL level. I cant see a QuerySet.and/or(QuerySet) method.
这显然会命中数据库两次并返回查询集的所有元素 - 更好的方法是将过滤器链接到您的管理器上,并且应该能够在一次数据库命中和 SQL 级别上完成。我看不到 QuerySet.and/or(QuerySet) 方法。
回答by lqc
In most cases you can just write (exploiting the "Set" part of QuerySet) :
在大多数情况下,您只需编写(利用 QuerySet 的“Set”部分):
intersection = Model.objects.filter(...) & Model.objects.filter(...)
This isn't very well documented, but should behave almost exactly like using AND conditions on conditions from both queries. Relevant code: https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203
这不是很好的记录,但应该表现得几乎完全像在两个查询的条件上使用 AND 条件一样。相关代码:https: //github.com/django/django/blob/1.8c1/django/db/models/query.py#L203
回答by Bernhard Vallant
If you want to do it in python, not in the database:
如果你想在 python 中执行它,而不是在数据库中:
intersection = set(queryset1) & set(queryset2)
The problems is that if you use different annotations in the queriesdue to the added annotations the objects might look different...
问题是,如果由于添加了注释而在查询中使用不同的注释,对象可能看起来不同......
回答by Jordan Reiter
If you really are just using annotation to filter based on whether the count is zero or not, then this should work instead:
如果您真的只是使用注释根据计数是否为零进行过滤,那么这应该可以工作:
class FeatureManager(models.Manager):
def without_test_cases(self):
return self.get_query_set().filter(testcase__pk__isnull=True)
def standardised(self):
return self.get_query_set().filter(documentation_set__standard__isnull=False)
Since you no longer are worrying about annotation, the two queries should intersect very smoothly.
由于您不再担心注释,因此两个查询应该非常顺利地相交。
回答by Alex Dehnert
I believe qs1.filter(pk__in=qs2) should work (usually). It seems to work for a similar case for me, it makes sense that it would work, and the generated query looks sane. (If one of your querysets uses values() to not select the primary key column or something weird, I can believe it'd break, though...)
我相信 qs1.filter(pk__in=qs2) 应该工作(通常)。它对我来说似乎适用于类似的情况,它可以工作是有道理的,并且生成的查询看起来很正常。(如果您的查询集之一使用 values() 来不选择主键列或一些奇怪的东西,我可以相信它会中断,但......)
回答by Caumons
You can just do something like this:
你可以做这样的事情:
intersection = queryset1 & queryset2
To do a union just replace &by |
做一个联合只需替换&为|
回答by tupan
As per Django 1.11, now it's available the function intersection()
根据Django 1.11,现在可以使用函数intersection()
>>> qs1.intersection(qs2, qs3)

