2011-01-06 37 views
19

Có thể lọc trong một chú thích không?Chú thích Django với bộ lọc lồng nhau

Trong một cái gì đó tâm trí của tôi như thế này (mà không thực sự làm việc)

Student.objects.all().annotate(Count('attendance').filter(type="Excused"))

Bảng kết quả sẽ có mỗi học sinh với số lượng vắng mặt có phép. Nhìn qua bộ lọc tài liệu chỉ có thể là trước hoặc sau chú thích sẽ không mang lại kết quả mong muốn.

Một cách giải quyết là này

for student in Student.objects.all(): 
    student.num_excused_absence = Attendance.objects.filter(student=student, type="Excused").count() 

này hoạt động nhưng không nhiều truy vấn, trong một ứng dụng thực tế này có thể nhận được không thực tế dài. Tôi nghĩ rằng loại tuyên bố này có thể trong SQL nhưng muốn ở lại với ORM nếu có thể. Tôi thậm chí đã cố gắng thực hiện hai truy vấn riêng biệt (một cho tất cả học sinh, một truy vấn khác để lấy tổng số) và kết hợp chúng với |. Sự kết hợp thay đổi tổng :(

Vài suy nghĩ sau khi câu trả lời đọc và bình luận của

tôi giải quyết vấn đề tham dự sử dụng thêm sql here.

  • Timmy's blog post là hữu ích. Câu trả lời của tôi là dựa tắt của nó.
  • câu trả lời của hash1baby hoạt động nhưng có vẻ phức tạp như sql.Nó cũng yêu cầu thực thi sql sau đó thêm kết quả trong vòng lặp for.Điều này là xấu đối với tôi vì tôi đang sắp xếp nhiều truy vấn lọc này cùng nhau. queryset w ith rất nhiều bộ lọc và thêm và thực hiện tất cả cùng một lúc.
  • Nếu hiệu suất không có vấn đề gì - tôi đề nghị vòng lặp for hoạt động xung quanh. Nó dễ hiểu nhất.
+3

Tôi đã viết một bài đăng blog về điều này vì nó không thể (afaik) mà không cần sử dụng SQL thô hoặc chỉ đơn giản là lặp lại trên các kết quả: http://timmyomahony.com/blog/filtering-annotations-django/ –

+0

Có một giải pháp mới cho điều này trong ORM như của Django 1.8. Xem câu trả lời của tôi dưới đây. – gordonc

Trả lời

13

Tính đến Django 1.8 bạn có thể làm điều này trực tiếp trong ORM:

students = Student.objects.all().annotate(num_excused_absences=models.Sum(
    models.Case(
     models.When(absence__type='Excused', then=1), 
    default=0, 
    output_field=models.IntegerField() 
))) 

trả lời chuyển thể từ another SO question on the same topic

tôi đã không kiểm tra ví dụ trên nhưng đã hoàn thành một cái gì đó tương tự trong ứng dụng của riêng tôi.

-3

lẽ điều này sẽ làm việc cho bạn:

excused = Student.objects.filter(attendance__type='Excused').annotate(abs=Count('attendance')) 

Bạn cần phải lọc các sinh viên bạn đang tìm kiếm đầu tiên để chỉ những người có vắng mặt có phép và sau đó chú thích các số trong số họ.

Đây là liên kết đến Django Aggregation Docs nơi thảo luận về thứ tự lọc.

+0

Vấn đề ở đây là lý do bây giờ là một danh sách các sinh viên với ít nhất một sự vắng mặt có lý do. Như đã đề cập, tôi nghĩ có thể có một số cách để kết hợp điều này, nhưng tôi đã không thể nghĩ ra một cách để làm điều này. "|" có thể kết hợp queryset nhưng nó có hiệu quả loại bỏ các bộ lọc do đó thay đổi số lượng. – Bufke

+0

đoán tôi không hiểu đúng yêu cầu. Tôi đã nhìn vào "Bảng kết quả sẽ có tất cả học sinh với số lần vắng mặt có lý do." Bạn đang tìm kiếm một tập kết quả có một mục nhập cho mỗi kết hợp học sinh/tham dự _type? – RyanBrady

+0

Có, như thế này sinh viên | Tổng số lần vắng mặt có lý do | Tổng số lần vắng mặt | Tổng số tiền cược hàng tháng, vv – Bufke

5

Bạn đúng - django không cho phép bạn lọc các đối tượng liên quan được đếm, mà không áp dụng bộ lọc cho các đối tượng chính, và do đó loại trừ các đối tượng chính đó mà không có đối tượng liên quan sau khi lọc.

Nhưng, trong một chút rò rỉ trừu tượng, bạn có thể đếm các nhóm bằng cách sử dụng truy vấn giá trị.

Vì vậy, tôi thu thập các lần vắng mặt trong từ điển và sử dụng nó trong vòng lặp. Một cái gì đó như thế này:

# a query for students 
students = Students.objects.all() 
# a query to count the student attendances, grouped by type. 
attendance_counts = Attendence(student__in=students).values('student', 'type').annotate(abs=Count('pk')) 
# regroup that into a dictionary {student -> { type -> count }} 
from itertools import groupby 
attendance_s_t = dict((s, (dict(t, c) for (s, t, c) in g)) for s, g in groupby(attendance_counts, lambda (s, t, c): s)) 
# then use them efficiently: 
for student in students: 
    student.absences = attendance_s_t.get(student.pk, {}).get('Excused', 0) 
+0

Điều này có phải là điều kiện chủng tộc nếu dữ liệu thay đổi giữa dòng 2 và dòng 4 không? – Gelatin

Các vấn đề liên quan