我有一个带有两个自定义管理器方法的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)
(testcase_set
和documentation_set
都是指其他型号上的ManyToManyField
。)
有没有办法获取查询集,或只是一个对象列表,这是每个管理器方法返回的查询集的交集?
答案 0 :(得分:50)
在大多数情况下,您只需编写(利用QuerySet的“Set”部分):
intersection = Model.objects.filter(...) & Model.objects.filter(...)
这个文档没有很好的记录,但其行为应该与在两个查询的条件下使用AND条件完全一样。相关代码:https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203
答案 1 :(得分:22)
你可以这样做:
intersection = queryset1 & queryset2
要进行联合,只需将&
替换为|
答案 2 :(得分:12)
根据Django 1.11,现在可以使用函数intersection()
>>> qs1.intersection(qs2, qs3)
答案 3 :(得分:4)
我相信qs1.filter(pk__in = qs2)应该有效(通常)。它对我来说似乎适用于类似的情况,它有意义,它会工作,生成的查询看起来很健全。 (如果你的一个查询集使用values()来不选择主键列或奇怪的东西,我可以相信它会破坏,但是......)
答案 4 :(得分:3)
重构
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() ) )
答案 5 :(得分:2)
如果你想在python中而不是在数据库中这样做:
intersection = set(queryset1) & set(queryset2)
问题在于,如果在querydue中对添加的注释使用不同的注释,则对象可能看起来不同......
答案 6 :(得分:0)
一种方法可能是使用python sets模块,只做一个交集:
制作几个在id = 5处重叠的查询集:
In [42]: first = Location.objects.filter(id__lt=6)
In [43]: last = Location.objects.filter(id__gt=4)
首先“导入集”(获取弃用警告......嗯......好吧)。现在构建并交叉它们 - 我们在集合中得到一个元素:
In [44]: sets.Set(first).intersection(sets.Set(last))
Out[44]: Set([<Location: Location object>])
现在获取交集元素的id来检查它是否真的是5:
In [48]: [s.id for s in sets.Set(first).intersection(sets.Set(last))]
Out[48]: [5]
这显然会使数据库两次点击并返回查询集的所有元素 - 更好的方法是在管理器上链接过滤器,并且应该能够在一个数据库命中和SQL级别执行此操作。我看不到QuerySet.and /或(QuerySet)方法。
答案 7 :(得分:0)
如果你真的只是使用注释根据计数是否为零进行过滤,那么这应该是有效的:
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)
由于您不再担心注释,因此两个查询应该非常平滑地相交。