Django фильтрация агрегированного запроса после агрегирования

У меня есть модель транзакции:

class transaction(models.Model):
    transactiondate = models.DateField()
    details = models.CharField(max_length=200, null=True)
    amount = models.DecimalField(decimal_places=2, max_digits=10)
    accountid = models.ForeignKey(account, on_delete=models.PROTECT)

Я пытаюсь получить текущий общий баланс на каждую дату, чтобы я мог построить баланс за последние 12 месяцев на линейной диаграмме. Мой запрос таков:

transaction.objects.annotate(
            balance=Window(Sum('amount'), order_by=F('transactiondate').asc())).filter(transactiondate__gte='2021-01-01')

Проблема в том, что это не включает данные до 2021-01-01 в расчет. Я хочу, чтобы сумма включала все транзакции независимо от даты, но я хочу видеть на графике только 12 месяцев. Я думал, что метка на фильтре после агрегата сначала рассчитает правильный баланс, а затем применит фильтр, чтобы дать мне только 12 месяцев, но это не то, что происходит. Если я удалю фильтр, то получу правильный баланс, но мой линейный график нечитабелен при таком количестве данных.

Есть ли способ фильтровать данные после агрегации?

Что-то вроде следующего должно работать с использованием Subquery. Производительность может быть хуже, чем при использовании оконного выражения

from django.db.models import Sum, OuterRef, Subquery

previous_transactions = Transaction.objects.filter(transactiondate__lte=OuterRef('transactiondate')).order_by().values('amount')
balances = previous_transactions.annotate(balance=Sum('amount')).values('balance')
annotated = Transaction.objects.order_by('transactiondate').annotate(balance=Subquery(balances)).filter(transactiondate__gte='2021-01-01')

Django выполняет group by с помощью values. Поэтому я думаю, что вам нужно сгруппировать по каждой дате, вы можете использовать следующий запрос.

import datetime
from django.db.models import Sum
from django.utils import timezone

transaction.objects.filter(
  transactiondate__gte=timezone.now().date()-timedeleta(months=12) # previous twelve months data
).values(
  'transactiondate' # this does the group by on date
).annotate(
  balance = Sum('amount') # aggregates the amount on each date
).values('transactiondate', 'balance')
Вернуться на верх