Агрегированные запросы Django с выражениями

у меня есть модель XYZ, и мне нужно получить максимальное значение для полей a, b и выражения x/y для данного queryset.

это прекрасно работает для полей. Что-то вроде:

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

... {'a__max': 10}

однако я не могу найти способ сделать это для выражений. Пытается что-то вроде:

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

выдает ошибку:

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

пытается что-то вроде:

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

выдает ошибку:

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

и еще что-то например:

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

также не работает и выдает ту же ошибку, что и выше:

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

один хак, который я нашел, чтобы сделать это:

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

который на самом деле работает, потому что он генерирует правильный SQL, но это сбивает с толку, потому что я получаю правильное значение в Z attribute, но не правильный экземпляр, тот, с этим максимальным значением.

конечно, я мог бы также использовать необработанные запросы или трюки с extra () и order_by (), но это действительно не имеет смысла для меня этот Django полностью поддерживает aggregate queries в хорошем смысле, но не может поддерживать выражения даже со своими собственными выражениями F.

есть ли способ сделать это?

4 ответов


в SQL, то, что вы хотите на самом деле

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;

таким образом, в 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]

версия

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

не имеет правильных x, y и экземпляра, потому что MAX функция оценивается среди всех строк, когда нет GROUP BY, таким образом, все экземпляры в возвращаемом QuerySet будут иметь одинаковое значение z as MAX(x/y).


ваш пример, который использует F() объекты должны работать нормально, так как Django 1.8:

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

есть фрагмент, который демонстрирует агрегацию с Sum() и F() объекты Django агрегация шпаргалка:

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

для версий ниже 1.8 вы можете достичь того же самого (недокументированным) способом.

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

это работает для Postgres, я не знаю о MySQL.

источник: агрегация Django: суммирование умножения двух полей


Я думаю, что вы должны сделать максимальные значения отдельно

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

а затем разделить на два поля

result['x__max'] \ result['y__max']