Чтобы понять это, нужно сначала изобразить таблицу:
column_a | column b | column_c
---------+----------+----------
1 | 2 | 3
4 | 5 | 6
7 | 8 | 9
Sum
- «вертикальная» операция; то есть, если бы мы хотели сумму column_a
, мы могли бы сделать
>>> DummyModel.objects.aggregate(total=Sum('column_a'))
{'total': 12}
Как видите, это возвращает 1 + 4 + 7 == 12
- так что вы можете понять, почему я называю это "вертикальной" суммой. Обратите внимание, что мы используем aggregate
вместо annotate
: aggregate
для вертикальных операторов.
Если вместо этого нам нужна «горизонтальная» сумма - сумма по строке - мы бы использовали F()
и +
. Таким образом, чтобы получить column_a + column_b
в каждом ряду, мы использовали бы
>>> DummyModel.objects.annotate(total=F('column_a') + F('column_b')).values('total')
<QuerySet [{'total': 3}, {'total': 9}, {'total': 15}]>
Надеюсь, вы понимаете, почему я называю это "горизонтальной" суммой: мы получаем сумму a
и b
"по горизонтали" в каждом ряду. И теперь обратите внимание, что мы используем annotate
, который предназначен для горизонтальных операций.
Если имена столбцов заранее не известны, вам нужно запутаться и использовать functools.reduce и operator.add для построения выражения:
>>> from functools import reduce
>>> from operator import add
>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.annotate(total=expr).values('total')
<QuerySet [{'total': 5}, {'total': 11}, {'total': 17}]>
Если нам нужна и горизонтальная и вертикальная сумма - т.е. сумма column_a
плюс сумма column_b
- нам нужно использовать Sum
и F()
:
>>> DummyModel.objects.aggregate(total=Sum(F('column_a') + F('column_b')))
{'total': 27}
Обратите внимание: aggregate
, а не аннотировать, так как мы в конечном итоге идем с вертикальной операцией; Sum
строк. Да, сначала выполняется горизонтальная операция, но, поскольку в конечном итоге мы Sum
, нам нужно aggregate
.
Итак, чтобы обернуть вещи, если поля являются переменными, нам нужно объединить aggregate
, Sum
и reduce
хитрость сверху:
>>> cols = ['column_b', 'column_c']
>>> expr = reduce(add, (F(col) for col in cols))
>>> DummyModel.objects.aggregate(total=Sum(expr))
{'total': 33}
Надеюсь, это поможет!