2010-12-10 69 views
18

我有两个自定义管理器方法的Django模型。每个都根据对象的不同属性返回模型对象的不同子集。如何找到两个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_setManyToManyField S于其他机型。)

有没有什么办法让一个查询集,或只是一个对象列表,这就是查询集的每一个返回的intersectiond经理方法?

+0

什么阻止你结合每个经理的两个过滤功能? – 2010-12-10 16:48:52

+0

你的意思是像'Model.objects.managerMethodOne()。managerMethodTwo()'?这似乎并不奏效。也许我没有正确写我的经理方法? – 2010-12-10 16:54:01

+3

过滤器功能本身。 'Model.objects.filter(这=那个).filter(即= somethingelse)'。你为什么不这样做? – 2010-12-10 16:56:11

回答

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())) 
+0

啊!是的,这很聪明,我认为我的设计可能是问题。 – 2010-12-11 12:49:27

+7

无论是否解决了问题,它仍然没有回答如何找到两个查询集的交集,这是谷歌在搜索“django查询集交集”时返回的第一个链接 – johannestaas 2014-09-22 23:21:20

0

一种方式可以是使用Python集模块,只是做一个交叉点:

使几个,在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)方法。

+1

千万不要使用'sets';它已被弃用,内置'set'('frozenset' for immutable)更好。 – 2010-12-22 07:33:17

40

在大多数情况下,你可以只写(利用“设置”查询集的一部分):

intersection = Model.objects.filter(...) & Model.objects.filter(...) 

这不是很好的记录,而应表现几乎完全一样使用和条件由两个条件查询。相关代码:https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203

+0

是的,我试过了,但它似乎没有工作。我似乎只是从较小的查询集中获取所有对象的查询集,包括那些不在较大的查询集中的对象。 – 2010-12-10 17:52:46

+0

你能做到以下几点:'intersection = Model.objects.filter(...)&Model.objects.filter(...)'然后'返回HttpResponse(“%s”%intersection.query)'这将会让它更容易弄清Django在将两个查询合并为一个时正在做什么。 – 2010-12-10 18:38:13

+0

这很好,但我无法获得唯一的行。 – 2016-10-26 07:17:50

2

如果你想这样做在python,而不是在数据库:

intersection = set(queryset1) & set(queryset2) 

的问题是,如果你在queriesdue使用不同的注释添加注释的对象可能有所不同...

0

如果你真的只是使用注释过滤器的基础上的计数是否是零或没有,那么这个建议立即进行删除d改为:

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) 

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

+0

啊,看看我不要认为'standardised'的查询工作。这将选择任何具有*一个*相关文档的特性* *不是*标准 - 而我希望它选择任何具有* no *相关文档的特性* *为*标准。 – 2010-12-11 13:07:46

4

我相信qs1.filter(pk__in = qs2)应该可以工作(通常)。它似乎适用于我的一个类似案例,这是有道理的,它会起作用,并且生成的查询看起来很理智。 (如果你的一个查询集使用values()不选择主键列或者奇怪的东西,我可以相信它会中断,尽管......)

19

你可以做这样的事情:

intersection = queryset1 & queryset2 

要做好工会只是|

+0

谢谢,它的工作原理!但它在切片查询集中不起作用 – 2016-12-29 06:57:08

4

按照Django的1.11更换&,现在是可用的功能intersection()

>>> qs1.intersection(qs2, qs3)