Is there a way to specify (in an annotation or aggregation) that a sequence of F
expressions should be summed together without manually typing out F("first_prop") + F("second_prop") + ...
?
I want something similar to how python's sum()
function allows you to pass an iterable and get the sum of the values in the iterable i.e. sum([1,2,3])
returns 6
.
Concretely, I want something that looks like this:
class Tree(TimeStampedModel):
leaf_count = models.IntegerField()
branch_count = models.IntegerField()
Tree.objects.create(leaf_count=60, branch_count=8)
Tree.objects.create(leaf_count=30, branch_count=3)
# now I want to annotate a combined count using my imaginary IterableSum aggregator
combined_sums = list(
Tree.objects.all().annotate(
combined_count=IterableSum(fields=[F("leaf_count"), F("branch_count")])
).values_list("combined_count", flat=True)
)
combined_sums # [68, 33]
How can I achieve this?
The only problem with sum
is that it starts with 0
as initial value. You can you can use reduce
from functools
:
from functools import reduce
from operator import add
from django.db.models import F
combined_sums = list(
Tree.objects.values(
combined_count=reduce(add, [F('leaf_count'), F('branch_count')]),
flat=True,
)
)
although strictly speaking, that is not even necessary, you can just use sum
, since it will add up 0
with F('leaf_count')
:
from django.db.models import F
combined_sums = list(
Tree.objects.values(
combined_count=sum([F('leaf_count'), F('branch_count')]),
flat=True,
)
)
Then this will have a + 0
in the query, which might not be ideal.
This is because sum
does not sum only integers, indeed, you can for example sum F
objects:
In [11]: sum([F('foo'), F('bar')])
Out[11]: <CombinedExpression: Value(0) + F(foo) + F(bar)>