Làm cách nào tôi có thể tìm thấy sự kết hợp của hai bộ truy vấn Django?


92

Tôi có một mô hình Django với hai phương pháp trình 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 nhau của đối tượng.

Có cách nào để lấy một bộ truy vấn, hoặc chỉ một danh sách các đối tượng, đó là sự kết hợp của các bộ truy vấn được trả về bởi mỗi phương thức trình quản lý?


3
(Từ một câu trả lời đã bị xóa) Xem câu hỏi này cho một sự thay đổi đó làm việc với queryset từ mô hình khác nhau: stackoverflow.com/questions/431628/...
rnevius

1
Bắt đầu từ phiên bản 1.11, bộ truy vấn django có một phương thức liên hợp nội trang. Tôi đã thêm nó như là một câu trả lời để tham khảo trong tương lai
Jose Cherian

Câu trả lời:


179

Điều này hoạt động và trông gọn gàng hơn một chút:

records = query1 | query2

Nếu bạn không muốn trùng lặp, thì bạn sẽ cần thêm .distinct():

records = (query1 | query2).distinct()

5
Mặc dù câu trả lời được chấp nhận trả về một liên hợp có thể lặp lại (chính xác là danh sách), giống như OP đã hỏi, phương thức này trả về một liên hợp thực sự của các tập truy vấn. Bộ truy vấn này có thể được vận hành thêm, điều này được mong muốn trong nhiều trường hợp.
Krystian Cybulski

5
Do lỗi Django, cấu trúc này đôi khi có thể trả về kết quả không chính xác khi xử lý ManyToManyFields. Ví dụ, đôi khi bạn sẽ thấy giá trị đó records.count()sẽ lớn hơn query1.count() + query2.count(), điều này rõ ràng là không chính xác.
Jian

4
@Jian, bạn có thể làm rõ phiên bản django với lỗi và liên kết đến vấn đề djangoproject không?
IMFletcher

10
bản ghi = query1 | truy vấn2; hồ sơ = records.distinct () sẽ cung cấp cho tôi những kết quả đúng
eugene

5
Bạn có thể nạp chồng các toán tử trong Python. Xem docs.python.org/2/library/operator.html . Vì vậy, những gì Django làm là tạo các phương thức đặc biệt cho đối tượng QuerySet. Xem mã ở đây: github.com/django/django/blob/master/django/db/models/... các QuerySetlớp cung cấp phương pháp cho __and____or__được gọi khi &hoặc |nhà khai thác đang được sử dụng giữa hai QuerySetđối tượng (cũng sử dụng cho Qlớp cũng ).
Jordan Reiter

49

Bắt đầu từ phiên bản 1.11 , các bộ truy vấn django có một phương thức kết hợp nội trang.

q = q1.union(q2) #q will contain all unique records of q1 + q2
q = q1.union(q2, all=True) #q will contain all records of q1 + q2 including duplicates
q = q1.union(q2,q3) # more than 2 queryset union

Xem bài đăng trên blog của tôi về điều này để biết thêm ví dụ.


Tôi không thể nhận được tất cả = Đúng để làm việc. Kết thúc việc truyền bộ truy vấn của tôi thành một bộ trước khi trả lại cho khách hàng.
Braden Holt

1
@BradenHolt, all = True, có nghĩa là nó sẽ chứa các bản ghi trùng lặp. Bạn chỉ cần loại bỏ tất cả = True để tránh truyền nó vào một tập hợp.
Jose Cherian

sau khi điều này không hoạt động DjangoFilterBackend, làm thế nào tôi có thể sử dụng union và DjangoFilterBackend?
nesalexy

Thật không may, điều này dường như không hoạt động đối với các mô hình có thứ tự mặc định được xác định trong Meta của mô hình. Bất cứ khi nào tôi cố gắng kết hợp chúng với .union, tôi nhận được lỗi sau: "ORDER BY không được phép trong truy vấn con của câu lệnh ghép".
jrial

4

Tôi sẽ đề xuất sử dụng 'query1.union (query2)' thay vì 'query1 | truy vấn2 '; Tôi nhận được kết quả khác với hai phương pháp trên và phương pháp trước là những gì tôi mong đợi. Sau đây là những gì tôi đã gặp:

print "union result:"
for element in query_set1.union(query_set2):
    print element

print "| result:"
for element in (query_set1 | query_set2):
    print element

kết quả:

union result:
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object
KafkaTopic object

| result:
KafkaTopic object
KafkaTopic object

1
Vui lòng dán mã, không phải hình ảnh của mã. Văn bản trong hình ảnh không thể tìm kiếm được, bạn không thể sao chép / dán nó vào trình chỉnh sửa của mình để xác minh và chiếm nhiều dung lượng hơn mức cần thiết. Sử dụng dấu gạch ngược để đánh dấu mã là mã, để mã được định dạng chính xác. Xem liên kết "trợ giúp" bên cạnh hộp nhập văn bản.
jrial

Cảm ơn đã cập nhật. :)
jrial
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.