2010-12-10 38 views
18

Tôi có mô hình Django với hai phương pháp quản lý tùy chỉnh. Mỗi trả về một tập hợp con khác nhau của các đối tượng của mô hình, dựa trên một thuộc tính khác của đối tượng.Làm thế nào tôi có thể tìm thấy giao điểm của hai truy vấn 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) 

(Cả testcase_setdocumentation_set tham khảo ManyToManyField s trên các mô hình khác.)

Có cách nào để có được một queryset, hay chỉ là một danh sách các đối tượng, đó là intersectiond của queryset trả về bởi mỗi phương pháp quản lý?

+0

Điều gì khiến bạn không thể kết hợp hai chức năng lọc của mỗi người quản lý? –

+0

Bạn có nghĩa là 'Model.objects.managerMethodOne(). ManagerMethodTwo()'? Điều đó dường như không hoạt động. Có lẽ tôi không viết chính xác phương thức quản lý của mình? –

+3

Bộ lọc tự hoạt động. 'Model.objects.filter (this = that) .filter (đó = cái gì đó)'. Tại sao bạn không làm điều đó? –

Trả lời

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! Đúng vậy, tôi nghĩ thiết kế của tôi có thể là vấn đề. –

+7

Cho dù nó sửa chữa vấn đề của mình hay không, nó vẫn không trả lời làm thế nào để tìm giao điểm của hai queryset, mà đây là liên kết đầu tiên mà google trả về trên một tìm kiếm cho "django intersection của querysets" – johannestaas

0

Một cách có thể sử dụng các mô-đun bộ python và chỉ cần làm một giao lộ:

thực hiện một vài bộ truy vấn mà chồng lên nhau tại id = 5:

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

"bộ nhập khẩu" đầu tiên (nhận được cảnh báo không dùng nữa ... ummm ... oh well). Bây giờ xây dựng và giao cho họ - chúng tôi có được một phần tử trong tập:

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

Bây giờ có được id của các yếu tố ngã để kiểm tra xem nó thực sự là 5:

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

Điều này rõ ràng lượt truy cập cơ sở dữ liệu hai lần và trả về tất cả các phần tử của bộ truy vấn - cách tốt hơn là kết nối các bộ lọc trên các trình quản lý của bạn và có thể thực hiện nó trong một lần truy cập DB và ở cấp SQL. Tôi không thể thấy một QuerySet.and/hoặc (QuerySet) phương pháp.

+1

Đừng bao giờ sử dụng 'bộ'; nó không được chấp nhận, 'set' (' frozenset' được xây dựng cho không thay đổi) được xây dựng tốt hơn. –

40

Trong hầu hết các trường hợp, bạn chỉ có thể viết (khai thác "Thiết" một phần của QuerySet):

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

Đây không phải là rất tốt tài liệu, nhưng nên cư xử gần như giống hệt như sử dụng AND điều kiện theo điều kiện từ cả hai truy vấn. mã có liên quan: https://github.com/django/django/blob/1.8c1/django/db/models/query.py#L203

+0

Vâng, tôi đã thử điều đó, nhưng nó dường như không hoạt động. Tôi dường như nhận được một queryset với tất cả các đối tượng từ queryset nhỏ hơn, bao gồm cả các queryset không nằm trong queryset lớn hơn. –

+0

Bạn có thể làm như sau: 'intersection = Model.objects.filter (...) & Model.objects.filter (...)' và sau đó 'return HttpResponse ("% s "% intersection.query)' Điều đó sẽ làm cho nó dễ dàng hơn để tìm ra những gì Django đang làm khi nó kết hợp hai truy vấn thành một. –

+0

điều này là tốt nhưng tôi không thể nhận được các hàng duy nhất. –

2

Nếu bạn muốn làm điều đó trong python, không phải trong cơ sở dữ liệu:

intersection = set(queryset1) & set(queryset2) 

Những vấn đề ở đây là nếu bạn sử dụng chú thích khác nhau trong queriesdue để các chú thích thêm các đối tượng có thể trông khác ...

0

Nếu bạn thực sự chỉ cần sử dụng chú thích để lọc dựa vào việc đếm là zero hay không, sau đó shoul này d làm việc thay thế:

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) 

Vì bạn không còn lo lắng về chú thích, nên hai truy vấn nên giao nhau rất thuận lợi.

+0

Ah, xem, tôi không nghĩ rằng truy vấn cho 'standardised' hoạt động. Điều đó chọn bất kỳ Tính năng nào có * một * Tài liệu liên quan * không phải là * một Chuẩn - trong khi tôi muốn nó chọn bất kỳ Tính năng nào có * không * tài liệu liên quan * là * Tiêu chuẩn. –

4

Tôi tin rằng qs1.filter (pk__in = qs2) sẽ hoạt động (thường). Dường như nó hoạt động cho một trường hợp tương tự đối với tôi, nó có ý nghĩa rằng nó sẽ hoạt động, và truy vấn được tạo ra trông có vẻ lành mạnh. (Nếu một trong các querysets của bạn sử dụng các giá trị() để không chọn cột khóa chính hoặc một cái gì đó kỳ lạ, tôi có thể tin rằng nó sẽ phá vỡ, mặc dù ...)

19

Bạn chỉ có thể làm điều gì đó như thế này:

intersection = queryset1 & queryset2 

Để làm được một liên minh chỉ cần thay thế & bởi |

+0

Cảm ơn, nó hoạt động! nhưng nó không hoạt động trong queryset đã cắt lát –

4

Theo Django 1.11, bây giờ nó có sẵn chức năng intersection()

>>> qs1.intersection(qs2, qs3) 
Các vấn đề liên quan