DJango ORM двойное соединение с Sum

Я искал подобный случай на SO и Google, но безуспешно.

КРАТКОЕ ОБЪЯСНЕНИЕ

У меня есть транзакции, которые принадлежат счету, а счет принадлежит объединению счетов. Я хочу получить список групп счетов с их счетами, и я хочу знать общий баланс каждого счета (баланс счета рассчитывается путем сложения суммы всех его транзакций).

ДЛИННОЕ ОБЪЯСНЕНИЕ

У меня есть следующие модели (для полноты картины я включаю миксины):

class UniqueNameMixin(models.Model):
    class Meta:
        abstract = True

    name = models.CharField(verbose_name=_('name'), max_length=100, unique=True)

    def __str__(self):
        return self.name


class PercentageMixin(UniqueNameMixin):
    class Meta:
        abstract = True

    _validators = [MinValueValidator(0), MaxValueValidator(100)]

    current_percentage = models.DecimalField(max_digits=5,
                                             decimal_places=2,
                                             validators=_validators,
                                             null=True,
                                             blank=True)

    ideal_percentage = models.DecimalField(max_digits=5,
                                           decimal_places=2,
                                           validators=_validators,
                                           null=True,
                                           blank=True)


class AccountsAggrupation(PercentageMixin):
    pass


class Account(PercentageMixin):
    aggrupation = models.ForeignKey(AccountsAggrupation, models.PROTECT)


class Transaction(models.Model):
    date = models.DateField()
    concept = models.ForeignKey(Concept, models.PROTECT, blank=True, null=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    account = models.ForeignKey(Account, models.PROTECT)
    detail = models.CharField(max_length=100, blank=True, null=True)

    def __str__(self):
        return '{} - {} - {} - {}'.format(self.date, self.concept, self.amount, self.account)

Я хочу иметь возможность сделать это в Django ORM:

select ca.*, ca2.*, sum(ct.amount)
from core_accountsaggrupation ca 
join core_account ca2 on ca2.aggrupation_id = ca.id 
join core_transaction ct on ct.account_id = ca2.id
group by ca2.name
order by ca.name;

Похоже, что вложенная навигация по множествам невозможна:

Неправильно: AccountsAggrupation.objects.prefetch_related('account_set__transaction_set')

(или любой аналогичный подход). Работать с этим можно следующим образом: перейти от транзакции к счету, а затем к account_aggroupation.

Но поскольку мне нужно было иметь дикту с account_aggroupation, указывающую каждому ключу на его набор счетов (и баланс для каждого), я в итоге сделал так:

def get_accounts_aggrupations_data(self):
    accounts_aggrupations_data = {}
    accounts_balances = Account.objects.annotate(balance=Sum('transaction__amount'))
    for aggrupation in self.queryset:
        aggrupations_accounts = accounts_balances.filter(aggrupation__id=aggrupation.id)
        aggrupation.balance = aggrupations_accounts.aggregate(Sum('balance'))['balance__sum']
        accounts_aggrupations_data[aggrupation] = aggrupations_accounts
    current_month = datetime.today().replace(day=1).date()
    date = current_month.strftime('%B %Y')
    total_balance = Transaction.objects.aggregate(Sum('amount'))['amount__sum']
    return {'balances': accounts_aggrupations_data, 'date': date, 'total_balance': total_balance}

Обратите внимание, что поскольку я перебираю accounts_aggrupations, этот запрос (self.queryset, который приводит к AccountsAggrupation.objects.all()) выполняется к БД.

Остальные запросы, которые я делаю, пока не выполняются, потому что я не итерирую их (пока не потребляю информацию в шаблоне).

Также обратите внимание, что словарь accounts_aggrupations_data имеет в качестве ключа объект accounts_aggrupation.

Вернуться на верх