如何找到两个Django查询集的交集?

时间:2010-12-10 16:47:23

标签: python django django-models

我有一个带有两个自定义管理器方法的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_setdocumentation_set都是指其他型号上的ManyToManyField。)

有没有办法获取查询集,或只是一个对象列表,这是每个管理器方法返回的查询集的交集?

8 个答案:

答案 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)

由于您不再担心注释,因此两个查询应该非常平滑地相交。