2010-12-10 10 views
18

Mam model Django z dwoma niestandardowymi metodami menedżera. Każdy zwraca inny podzbiór obiektów modelu, w oparciu o inną właściwość obiektu.Jak mogę znaleźć przecięcie dwóch zestawów zapytań 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) 

(Zarówno testcase_set i documentation_set patrz ManyToManyField s na innych modelach.)

Czy istnieje jakiś sposób, aby uzyskać queryset, lub po prostu listę obiektów, które jest intersectiond z querysets zwracane przez każdy metoda menedżera?

+0

Co powstrzymuje Cię przed połączeniem dwóch funkcji filtrowania z każdym menedżerem? –

+0

Masz na myśli jak 'Model.objects.managerMethodOne(). ManagerMethodTwo()'? To nie działa. Może nie napisałem poprawnie swojej metody zarządzania? –

+3

Filtr działa sam. 'Model.objects.filter (this = that) .filter (that = somethingelse)'. Dlaczego tego nie robisz? –

Odpowiedz

3

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

Ah! Tak, to sprytnie, myślałem, że mój projekt może być problemem. –

+7

Niezależnie od tego, czy naprawia problem, czy nie, wciąż nie odpowiada na to, jak znaleźć przecięcie dwóch zestawów zapytań, do których był pierwszym linkiem, który został zwrócony przez google przy wyszukiwaniu "skrzyżowania django w zestawach zapytań". – johannestaas

0

Jednym ze sposobów, może być użyta do modułu zestawów pytona i tylko do skrzyżowania:

zrobić kilka zestawów zapytania, które zachodzą na siebie, J = 5:

In [42]: first = Location.objects.filter(id__lt=6) 
In [43]: last = Location.objects.filter(id__gt=4) 

„ustawia import” pierwsze (dostaje ostrzeżenie o zwolnieniu ... ummm ... no cóż). Teraz budować i przecinają je - mamy jeden z elementów zestawu:

In [44]: sets.Set(first).intersection(sets.Set(last)) 
Out[44]: Set([<Location: Location object>]) 

teraz uzyskać identyfikator elementów skrzyżowań, by sprawdzić to naprawdę jest 5:

In [48]: [s.id for s in sets.Set(first).intersection(sets.Set(last))] 
Out[48]: [5] 

To oczywiście uderza bazy dwukrotnie i zwraca wszystkie elementy zestawu zapytań - lepszym sposobem byłoby powiązanie filtrów z twoimi menedżerami i powinno być to możliwe w jednym hicie DB i na poziomie SQL. Nie widzę metody QuerySet.and/lub (QuerySet).

+1

Nigdy nie używaj 'sets'; jest przestarzałe, wbudowany 'set' (' frozenset' for immutable) jest lepszy. –

40

W większości przypadków można po prostu napisać (wykorzystując „Set” część queryset):

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

To nie jest bardzo dobrze udokumentowane, ale powinien zachowywać się niemal dokładnie tak samo jak i warunków korzystania z obydwu warunków zapytania. Odpowiedni kod: https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203

+0

Tak, próbowałem, ale wydawało się, że to nie działa. Wydaje mi się, że otrzymałem zestaw zapytań ze wszystkimi obiektami z mniejszego zestawu zapytań, w tym te, które nie znajdowały się w większym zapytaniu. –

+0

Czy możesz wykonać następujące czynności: 'przecięcie = Model.objects.filter (...) & Model.objects.filter (...)', a następnie 'return HttpResponse ("% s "% przecięcie.query)' To będzie łatwiej zrozumieć, co robi Django, gdy łączy dwa zapytania w jeden. –

+0

to jest w porządku, ale nie mogłem uzyskać unikalnych wierszy. –

2

Jeśli chcesz to zrobić w Pythonie, a nie w bazie danych:

intersection = set(queryset1) & set(queryset2) 

problemów jest to, że jeśli używasz różnych adnotacji w queriesdue na dodatkowe adnotacje obiekty mogą wyglądać inaczej ...

0

Jeśli naprawdę są tylko za pomocą adnotacji do filtrować na podstawie tego, czy liczba jest równa zero lub nie, to shoul zamiast tego działa:

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) 

Ponieważ nie martwisz się już o adnotację, te dwa pytania powinny być bardzo płynne.

+0

Ah, zobacz, I nie myśl, że zapytanie o 'standardised' działa. To wybiera dowolną Cechę, która ma * jedną * powiązaną Dokumentację, która * nie jest * Standardem - podczas gdy ja chcę, aby wybrała jakąkolwiek Cechę, która ma * nie * powiązane Dokumentacje, które * są * Standardami. –

4

Uważam, że qs1.filter (pk__in = qs2) powinien działać (zwykle). Wydaje się, że działa to w podobnym dla mnie przypadku, ma sens, żeby zadziałało, a wygenerowane zapytanie wygląda na zdrowe. (Jeśli któryś z twoich zestawów zapytań używa wartości(), aby nie wybrać kolumny klucza głównego lub czegoś dziwnego, mogę uwierzyć, że się zepsuł, chociaż ...)

19

Można po prostu zrobić coś takiego:

intersection = queryset1 & queryset2 

zrobić unia prostu zastąpić & przez |

+0

Dzięki, to działa! ale nie działa w plasterkach kwerendy –

Powiązane problemy