2012-04-19 36 views
7

Tôi có mô hình XYZ và tôi cần lấy giá trị lớn nhất cho các trường a, b và biểu thức x/y cho một tập hợp truy vấn đã cho.Truy vấn tổng hợp Django có biểu thức

Nó hoạt động tốt cho các trường. Một cái gì đó như:

>>> XYZ.all().aggregate(Max('a')) 

... {'a__max': 10} 

Tuy nhiên, tôi không thể tìm cách làm điều đó cho biểu thức. Cố gắng một cái gì đó như:

>>> XYZ.all().aggregate(Max('x/y')) 

Cung cấp cho một lỗi:

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id 

cố gắng một cái gì đó như:

>>> XYZ.all().aggregate(Max(F('x')/F('y'))) 

Cung cấp cho một lỗi:

*** AttributeError: 'ExpressionNode' object has no attribute 'split' 

Và ngay cả cái gì đó như:

XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z')) 

Cũng không hoạt động và cung cấp cho các lỗi tương tự như trên:

FieldError: Cannot resolve keyword 'z' into field. Choices are: a, b, x, y, id 

Một hack tôi tìm thấy để làm điều đó là:

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z 

nào thực sự hoạt động vì nó tạo ra đúng SQL, nhưng nó gây nhầm lẫn bởi vì tôi nhận được giá trị đúng tại thuộc tính z, nhưng không phải là một cá thể đúng, cái có giá trị tối đa đó.

Tất nhiên, tôi cũng có thể sử dụng truy vấn thô hoặc thủ thuật với thêm() và order_by(), nhưng nó thực sự không có ý nghĩa với tôi mà Django đi tất cả các cách để hỗ trợ các truy vấn tổng hợp một cách tốt đẹp, nhưng không thể hỗ trợ các biểu thức ngay cả với các biểu thức F của riêng nó.

Có cách nào để làm điều đó không?

+0

Bạn có thể quan tâm để biết rằng khả năng sử dụng 'F()' đối tượng trong uẩn là [một phần của Django 1.8 sắp phát hành] (https://code.djangoproject.com/ wiki/Version1.8Roadmap). –

Trả lời

6

Trong SQL, những gì bạn muốn thực sự là

SELECT x/y, * FROM XYZ ORDER BY x/y DESC LIMIT 1; 
# Or more verbose version of the #1 
SELECT x/y, id, a, b, x, y FROM XYZ GROUP BY x/y, id, a, b, x, y ORDER BY x/y DESC LIMIT 1; 
# Or 
SELECT * FROM XYZ WHERE x/y = (SELECT MAX(x/y) FROM XYZ) LIMIT 1; 

Như vậy trong Django ORM:

XYZ.objects.extra(select={'z':'x/y'}).order_by('-z')[0] 
# Or 
XYZ.objects.extra(select={'z':'x/y'}).annotate().order_by('-z')[0] 
# Or x/y=z => x=y*z 
XYZ.objects.filter(x=models.F('y') * XYZ.objects.extra(select={'z':'MAX(x/y)'})[0].z)[0] 

Phiên bản

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z 

không có đúng x, y và dụ bởi vì chức năng MAX được đánh giá trong tất cả các hàng, khi không có GROUP BY, do đó tất cả các trường trong QuerySet trở sẽ có cùng một giá trị của zMAX(x/y).

+0

Đúng, nhưng mục đích là để có được giá trị tối đa, khi trả về từ XYZ.all(). aggregate (Max ('a')), không phải là cá thể chứa nó. Phiên bản có thêm lựa chọn là gần nhất với nó. Không trả về đúng cá thể là một tác dụng phụ khó hiểu, nhưng nó trả về giá trị đúng. Như tôi đã nói trong thông điệp mở đầu, tôi nhận thức được các giải pháp có thêm và order_by, nhưng chúng không thể chấp nhận được vì chúng yêu cầu một loại bảng đầy đủ, không phải là một lần. Nó không có ý nghĩa nhiều cho Django để hỗ trợ Max tổng hợp với các lĩnh vực duy nhất nhưng không phải là biểu thức. –

+0

@pjwerneck Lý do của tác dụng phụ khó hiểu mà bạn gọi, được mô tả trong đoạn cuối cùng của câu trả lời của tôi. Nếu bạn chỉ muốn giá trị lớn nhất, 'XYZ.objects.extra (select = {'z': 'MAX (x/y)'}) [0] .z' là đủ, không có order_by. Hoặc thậm chí trực tiếp 'cursor.execute ('SELECT MAX (x/y) từ XYZ')'. Tôi đồng ý w/bạn rằng Django không cung cấp tổng hợp w/biểu thức, bởi vì nó có thể được đáng kể khó khăn hơn so với hỗ trợ duy nhất lĩnh vực IMO. – okm

-3

Tôi nghĩ rằng bạn sẽ nhận được các giá trị tối đa riêng

result = XYZ.aggregate(Max('x'), Max('y')) 

Và sau đó chia hai trường

result['x__max'] \ result['y__max'] 
+0

Điều đó không có ý nghĩa gì cả. Ngay cả khi nó trả về hàng với cặp tối đa x và y, thì đó không nhất thiết phải là x/y tối đa. Ví dụ, hàng Max (x)/Max (y) là 69/16 = 4, trong khi max (x/y) là 8/1 = 8 –

2

Ví dụ của bạn sử dụng các đối tượng F() sẽ hoạt động tốt kể từ Django 1.8:

XYZ.all().aggregate(Max(F('x')/F('y'))) 

Có một đoạn thể hiện sự kết hợp với Sum()F() đối tượng trong Django aggregation cheat sheet:

Book.objects.all().aggregate(price_per_page=Sum(F('price')/F('pages')) 
+0

Điều cần biết. Cảm ơn! –

0

Đối với phiên bản thấp hơn 1.8, bạn có thể đạt được như vậy trong việc này (không có giấy tờ) cách.

Book.objects.all().aggregate(price_per_page=Sum('price_per_page', 
               field='book_price/book_pages')) 

Điều này phù hợp với Postgres, tôi không biết về MySQL.

Nguồn: Django Aggregation: Summation of Multiplication of two fields

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