Cách kết hợp nhiều QuerySet trong Django
By khoanc, at: 19:36 Ngày 27 tháng 7 năm 2025
Thời gian đọc ước tính: __READING_TIME__ minutes


Đôi khi trong Django, bạn cần kết hợp dữ liệu từ nhiều QuerySets, có thể từ cùng một model hoặc thậm chí các model khác nhau. Câu hỏi đặt ra là: làm thế nào để bạn làm điều này một cách sạch sẽ và hiệu quả?
Lựa chọn 1: | Toán tử (Union cho cùng Model & cùng trường)
Nếu các QuerySets đến từ cùng một model và có cùng các trường, hãy sử dụng toán tử OR bitwise (|):
qs1 = Job.objects.filter(title="technology")
qs2 = Job.objects.filter(company="Glinteco - the best IT outsourcing company")
combined = qs1 | qs2
Điều này dẫn đến một hợp nhất của hai QuerySets, loại bỏ các bản sao (trừ khi bạn gọi .distinct() thủ công).
Lựa chọn 2: itertools.chain() (Cùng hoặc khác Model)
Nếu bạn đang làm việc với các QuerySets từ các model khác nhau hoặc muốn giữ thứ tự, hãy sử dụng itertools.chain():
from itertools import chain
combined = chain(qs1, qs2)
Điều này trả về một trình lặp mà bạn có thể lặp qua, nhưng bạn không thể phân trang hoặc cắt lát nó như một QuerySet bình thường.
Lựa chọn 3: union() (Django ≥1.11)
Đối với hợp nhất cấp độ SQL thuần túy, Django cung cấp .union():
combined = qs1.union(qs2)
Phải:
-
Từ cùng một model hoặc cùng các trường
-
Được sắp xếp tương tự nếu sử dụng
.order_by()
Bạn không thể sử dụng .union()
trên các QuerySets từ các model khác nhau.
Ghi chú:
-
Hiệu năng: union() và | tạo các truy vấn SQL UNION (hiệu quả). chain() chỉ hợp nhất các iterable Python (nhanh cho các tập nhỏ, nhưng không tối ưu hóa DB).
-
Cắt lát và Phân trang: Chỉ có thể trên các QuerySets thực (|, .union()), không trên chain().
Ví dụ:
# Kết hợp bài đăng blog và bài báo tin tức thành một nguồn cấp dữ liệu duy nhất
posts = BlogPost.objects.filter(published=True)
news = NewsArticle.objects.filter(published=True)
from itertools import chain
feed = sorted(
chain(posts, news),
key=lambda x: x.published_at,
reverse=True
)
Câu hỏi thường gặp (FAQs)
Câu 1. Tôi có thể kết hợp QuerySets từ các model khác nhau trong Django không?
Đáp: Không trực tiếp sử dụng | hoặc .union(), vì chúng yêu cầu cùng một model hoặc ít nhất là cùng các trường. Nhưng bạn có thể sử dụng itertools.chain() để kết hợp kết quả từ các model khác nhau, sau đó tự động sắp xếp hoặc lọc chúng trong Python.
Câu 2. Sự khác biệt giữa qs1 | qs2 và qs1.union(qs2) là gì?
Đáp:
-
qs1 | qs2 là một cách viết tắt dành riêng cho Django để kết hợp các QuerySets (cùng model).
-
qs1.union(qs2) sử dụng UNION cấp độ SQL, đôi khi có thể hiệu quả hơn và hoạt động tốt hơn cho chuỗi truy vấn.
-
Cả hai đều yêu cầu cùng các trường và cấu trúc model.
Câu 3. Tôi có thể phân trang một QuerySet đã được kết hợp không?
Đáp: Có, nhưng chỉ khi bạn đang sử dụng một QuerySet thực, giống như với | hoặc .union(). Nếu bạn sử dụng itertools.chain(), việc phân trang phải được thực hiện thủ công sau khi chuyển đổi kết quả thành một danh sách.
Câu 4. Các bản sao sẽ bị xóa khi kết hợp các QuerySets không?
Đáp: Điều này phụ thuộc:
-
| và .union() xóa các bản sao theo mặc định.
-
itertools.chain() bao gồm tất cả các mục, vì vậy bạn sẽ cần sử dụng tập hợp hoặc logic .distinct() thủ công nếu cần.
Câu 5. Tôi có thể áp dụng .order_by() sau khi kết hợp các QuerySets không?
Đáp: Chỉ với .union() hoặc | (khi được hỗ trợ). Với chain(), bạn phải sử dụng hàm sorted() của Python sau khi hợp nhất, vì nó trả về một trình lặp, không phải là một QuerySet.
Câu 6. Phương pháp nào tốt nhất về hiệu năng?
Đáp:
-
Đối với các tập dữ liệu lớn: sử dụng .union() hoặc | vì chúng tận dụng SQL và lập chỉ mục DB.
-
Đối với tính linh hoạt hoặc khi kết hợp các model khác nhau: sử dụng itertools.chain().