2010-12-10 88 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)