2012-04-19 21 views
7

Mam model XYZ i potrzebuję uzyskać maksymalną wartość dla pól a, b i wyrażenie x/y dla danego zestawu zapytań.Kwerendy agregujące Django z wyrażeniami

Działa pięknie na pola. Coś jak:

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

... {'a__max': 10} 

Jednak nie mogę znaleźć sposobu na wyrażenie. Próbuje coś takiego:

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

daje błąd:

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

próbuje coś takiego:

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

daje błąd:

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

a nawet coś takiego:

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

również nie działa i daje ten sam błąd jak wyżej:

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

jednej hack, znalazłem to zrobić to:

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

co faktycznie działa, ponieważ generuje odpowiedni SQL, ale jest to mylące, ponieważ otrzymuję właściwą wartość w atrybucie z, ale nie w prawidłowej instancji, tej z tą maksymalną wartością.

Oczywiście, mogę również użyć surowych zapytań lub trików z dodatkowymi() i order_by(), ale to naprawdę nie ma sensu dla mnie, że Django idzie do samego końca, aby wesprzeć zagregowane zapytania w miły sposób, ale nie może obsługiwać wyrażeń nawet z własnymi wyrażeniami F.

Czy jest jakikolwiek sposób to zrobić?

+0

Możesz być zainteresowany, aby wiedzieć, że umiejętność korzystania 'F()' obiekty agregatów jest [częścią nadchodzącego Django 1.8 Release] (https://code.djangoproject.com/ wiki/Version1.8Roadmap). –

Odpowiedz

6

W SQL, co chcesz, to faktycznie

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; 

Zatem w 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] 

Wersja

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

nie ma poprawny x, y i przykład ponieważ funkcja MAX jest oceniane u wszystkich rzędach, gdy nie ma GROUP BY, co wszystkich przypadkach, w zwróconej queryset będzie miało taką samą wartość z jako MAX(x/y).

+0

Dobrze, ale celem jest uzyskanie maksymalnej wartości, jako zwrotu z XYZ.all(). aggregate (Max ("a")), a nie instancja go zawierająca. Wersja z dodatkowym wyborem jest najbliżej tego. Niezwrócenie właściwej instancji jest mylącym efektem ubocznym, ale zwraca właściwą wartość. Jak już wspomniałem w komunikacie otwierającym, jestem świadomy rozwiązań z dodatkowymi i order_by, ale są one niedopuszczalne, ponieważ wymagają pełnego rodzaju tabeli, a nie pojedynczego przejścia. Django nie ma większego sensu, aby obsługiwać agregaty Max z pojedynczymi polami, ale bez wyrażeń. –

+0

@pjwerneck Powód mylenia niepożądanego efektu, który wywołałeś, jest opisany w ostatnim akapicie mojej odpowiedzi. Jeśli chcesz tylko wartość maksymalną, 'XYZ.objects.extra (select = {'z': 'MAX (x/y)'}) [0] .z' wystarczy, nie ma zlecenia. Lub nawet bezpośrednio 'cursor.execute ('SELECT MAX (x/y) od XYZ')'. Zgadzam się z tym, że Django nie zapewnia zagregowanych wyrażeń w /, ponieważ może to być znacznie trudniejsze niż wspieranie pojedynczego pola IMO. – okm

-3

myślę, że należy uzyskać maksymalne wartości osobno

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

a następnie podzielić na dwa pola

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

To nie ma żadnego sensu. Nawet jeśli zwraca wiersz z maksymalną parą xiy, niekoniecznie jest to maksymalna wartość x/y. Na przykład, wiersz Max (x)/Max (y) to 69/16 = 4, natomiast max (x/y) to 8/1 = 8 –

2

Twój przykład, który używa obiektów F() powinien działać poprawnie od Django 1.8:

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

Jest to fragment, który demonstruje agregację z Sum() i F() obiektów w Django aggregation cheat sheet:

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

Dobrze wiedzieć. Dzięki! –

Powiązane problemy