Использование extra () для ValuesQuerySet в Django - PullRequest
8 голосов
/ 04 марта 2011

Я пытаюсь рассчитать процент с двумя значениями, которые сами агрегируются. 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

Если вы используете предложение values ​​() после предложения extra (), любые поля, определенные аргументом select в extra (), должны быть явно включены в предложение values ​​(). Однако, если предложение extra () используется после values ​​(), поля, добавленные в select, будут включены автоматически.

Источник: http://docs.djangoproject.com/en/dev/ref/models/querysets/#values

Ответы [ 3 ]

3 голосов
/ 06 сентября 2017

Агрегированные выражения разрешают такие выражения для агрегатных функций легко , так как 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 ​​() не будет работать - commit a4a250a .)

0 голосов
/ 13 марта 2011

Если вы используете предложение values ​​() после предложения extra (), любые поля, определенные аргументом select в extra (), должны быть явно включены в предложение values ​​().

Источник: http://docs.djangoproject.com/en/dev/ref/models/querysets/#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')
0 голосов
/ 11 марта 2011

Как вы указали ( # 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')]

Поскольку это решение вынуждает вас перебирать все данные, в зависимости от того, что вы хотите сделать, это может или не может быть приемлемо.

...