Использование extra() для ValuesQuerySet в Django
Я пытаюсь рассчитать процент с двумя значениями, которые сами агрегированы. SQL-запрос, который объясняет, что я ищу, выглядит следующим образом:
SELECT (SUM(field_a) / SUM(field_b) * 100) AS percent
FROM myapp_mymodel
GROUP BY id
ORDER BY id
Я попытался использовать следующее для создания QuerySet, но, к сожалению, он не содержит дополнительного поля:
MyModel.objects.values('id').annotate(
sum_field_a=Sum('field_a'),
sum_field_b=Sum('field_b')).extra(
select={'percent': 'sum_field_a / sum_field_b * 100'})
Что меня раздражает, так это то, что, согласно документации Django, кажется, что это путь:
Когда предложение values () используется для ограничения столбцов, возвращаемых в наборе результатов […], вместо возврата аннотированного результата для каждого результата в исходном QuerySet, исходные результаты группируются в соответствии с уникальными комбинациями указанных полей в предложении values (). Затем предоставляется аннотация для каждой уникальной группы; аннотация рассчитывается для всех членов группы.
Источник: http://docs.djangoproject.com/en/dev/topics/db/aggregation/
Если вы используете предложение values () после предложения extra (), любые поля, определенные аргументом select в extra (), должны быть явно включены в предложение values (). Однако, если предложение extra () используется после values (), поля, добавленные выбором, будут включены автоматически.
Источник: http://docs.djangoproject.com/en/dev/ref/models/querysets/
3 ответа
Агрегированные выражения допускают такие выражения в агрегатных функциях легко, начиная с Django 1.8, без проблемного метода extra ().
qs = (
MyModel.objects.values('id')
.annotate(percent=Sum('field__a') / Sum('field__b') * 100)
.order_by('id')
)
>>> print(str(qs.query))
SELECT id, ((SUM(field_a) / SUM(field_b)) * 100) AS percent
FROM app_mymodel GROUP BY id ORDER BY id ASC
(Упомянутая проблема #15546 была вскоре закрыта примечанием к документации, что extra () после values () не будет работать - зафиксируйте a4a250a.)
Если вы используете предложение values () после предложения extra(), любые поля, определенные аргументом select в extra(), должны быть явно включены в предложение values ().
Источник: http://docs.djangoproject.com/en/dev/ref/models/querysets/
поле "проценты", добавленное в select, может быть явно добавлено в предложение values, и оно должно быть добавлено в набор запросов.
MyModel.objects.annotate(
sum_field_a=Sum('field_a'),
sum_field_b=Sum('field_b')).extra(
select={'percent': 'sum_field_a / sum_field_b * 100'}
).values('id', 'percent')
Как вы указали ( # 15546), в Django может быть ошибка.
Но в качестве обходного пути вы можете возложить бремя фактических вычислений на python вместо базы данных SQL, выполнив что-то вроде этого:
[{'field_c': model['field_c'],
'percent': m['sum_field_a'] * 100.0 / m['sum_field_b']}
for model in MyModel.objects.values('field_c').annotate(
sum_field_a=Sum('field_a'),
sum_field_b=Sum('field_b')).order_by('field_c')]
Поскольку это решение вынуждает вас перебирать все данные, в зависимости от того, что вы хотите сделать, это может быть или не быть приемлемым.