Ev.
Ev.

Reputation: 1567

Why annotate generates duplicate entries?

Given the following code:

payments = Payment.objects.filter(
        customer=self.customer,
        created_at__gte=kwargs['start_date'],
        created_at__lte=kwargs['end_date']
    ).order_by('-date')

balance = payments.values('currency').annotate(Sum('amount'))

> print(balance)
> [{'amount__sum': Decimal('0.00'), 'currency': 'USD'},
   {'amount__sum': Decimal('0.00'), 'currency': 'USD'},
   {'amount__sum': Decimal('9000.00'), 'currency': 'SEK'},
   {'amount__sum': Decimal('45000.00'), 'currency': 'EUR'},
   {'amount__sum': Decimal('11385.00'), 'currency': 'SEK'}]

List of payments for this customer:

> print(payments)
> ('-1487.50', 'USD')
('1487.50', 'USD')
('-3663.72', 'USD')
('3663.72', 'USD')
('15000.00', 'EUR')
('9000.00', 'SEK')
('30000.00', 'EUR')
('9865.00', 'SEK')
('1520.00', 'SEK')

If I use aggregate, I get the Sum, but for all currencies and that's not what I want. I must be able to split into currencies.

{'amount__sum': Decimal('65385.00')}

I am trying to extract the payments by customer grouping and summing by currencies. However what happens is that it doesn't Sum some of the values instead duplicating them. Any ideas?

Upvotes: 5

Views: 5539

Answers (1)

It is the order_by that causes this. Fields mentioned in order_by will implicitly be part of the group otherwise defined as the fields mentioned in values according to the docs on aggregation and order_by.

See if

Payment.objects.filter(
        customer=self.customer,
        created_at__gte=kwargs['start_date'],
        created_at__lte=kwargs['end_date']
    ).values('currency').annotate(Sum('amount'))

won't give you exactly the answer you are looking for.

This will also be caused by a default ordering set on Payment.Meta.ordering. If you did that you will need to cancel out the ordering explicitly like so:

Payment.objects.filter(
        customer=self.customer,
        created_at__gte=kwargs['start_date'],
        created_at__lte=kwargs['end_date']
    ).order_by().values('currency').annotate(Sum('amount'))

Upvotes: 7

Related Questions