Агрегированные запросы 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']