Django 用 Sum 进行多重注释得到错误的答案

Ahm*_*amy 2 python django orm django-models

我试图.annotate()与多个一起使用Sum()但我得到了错误的计算。我读过我应该使用它Subquery,但我没有用它完成它,也许我以错误的方式使用它(因为这是第一次)或者它不能解决我的问题。

#managers.py

class DonationQuerySet(QuerySet):

    def completed(self):
        return self.with_donations_stats().filter(
            amount__lte=F('total_donation'), deleted_at=None)

    def not_completed(self):
        return self.with_donations_stats().filter(
            amount__gt=F('total_donation'), deleted_at=None)

    def with_donations_stats(self):
        return self.annotate(
            wallet_donation=Coalesce(Sum('wallet_transaction__amount'), 0),
            normal_donation=Coalesce(Sum('transactions__amount'), 0),
            total_donation=F('wallet_donation') + F('normal_donation'))


class DonationManager(Manager):
    def get_queryset(self):
        return DonationQuerySet(self.model, using=self._db)

    def completed(self):
        return self.get_queryset().completed()

    def not_completed(self):
        return self.get_queryset().not_completed()

    def with_donations_stats(self):
        return self.get_queryset().with_donations_stats()
Run Code Online (Sandbox Code Playgroud)
#models.py

class Transaction(models.Model):
    def __str__(self):
        return self.payment_id + ' - ' + self.status

    condition = models.ForeignKey('condition.Condition', related_name='transactions',
                                  on_delete=models.CASCADE) 
    
    amount = models.IntegerField(null=False, blank=False)

class WalletTransaction(AbstractBaseModel):
    condition = models.ForeignKey("condition.Condition", on_delete=models.SET_NULL, related_name='wallet_transaction', null=True)
    amount = models.PositiveIntegerField()

    def __str__(self):
        return f"{self.id}"

class Condition(models.Model):
    def __str__(self):
        return str(self.id)  # .zfill(10)
    STATUS_PUBLISHED = "Published"
    STATUS_CHOICES = (
        (STATUS_PUBLISHED, 'Published'),)
    status = models.CharField(max_length=25, choices=STATUS_CHOICES, db_index=True)

    donations = DonationManager()
Run Code Online (Sandbox Code Playgroud)

在我看来,我正在查询已发布的条件条件=

Condition.donations.filter(status=Condition.STATUS_PUBLISHED).with_donations_stats().order_by(
            '-id')
Run Code Online (Sandbox Code Playgroud)

更新 最终查询是SELECT "conditions"."id", "conditions"."user_id", "conditions"."association_id", "conditions"."nid", "conditions"."amount", "conditions"."donation", "conditions"."nid_type", "conditions"."first_name", "conditions"."father_name", "conditions"."last_name", "conditions"."nationality", "conditions"."gender", "conditions"."mobile", "conditions"."region", "conditions"."city", "conditions"."district", "conditions"."dob", "conditions"."introduction_file", "conditions"."disease_validation_file", "conditions"."medical_report_file", "conditions"."treatment_plan_file", "conditions"."cost_file", "conditions"."case_report_file", "conditions"."invoices_file", "conditions"."payment_file", "conditions"."generated_pdf_file", "conditions"."recovery_report_file", "conditions"."report_date", "conditions"."issued_place", "conditions"."specialization", "conditions"."disease_type", "conditions"."action_type", "conditions"."case_type", "conditions"."treatment_entity", "conditions"."accommodation_type", "conditions"."family_members", "conditions"."income_avg", "conditions"."medical_evaluation_status", "conditions"."researcher_opinion", "conditions"."rejection_reason", "conditions"."justification", "conditions"."insurance_company_name_ar", "conditions"."insurance_company_name_en", "conditions"."insurance_company_id", "conditions"."insurance_beneficiary_number", "conditions"."insurance_beneficiary_type", "conditions"."insurance_class", "conditions"."insurance_expiry", "conditions"."insurance_limit", "conditions"."insurance_policy", "conditions"."status", "conditions"."created_at", "conditions"."updated_at", "conditions"."published_at", "conditions"."deleted_at", "conditions"."image", "conditions"."last_donation_at", COALESCE((SELECT SUM(U0."amount") AS "amount_sum" FROM "wallets_wallettransaction" U0 WHERE U0."condition_id" = ("conditions"."id") GROUP BY U0."id" ORDER BY U0."id" DESC LIMIT 1), 0) AS "wallet_donation", COALESCE((SELECT SUM(U0."amount") AS "amount_sum" FROM "transactions" U0 WHERE U0."condition_id" = ("conditions"."id") GROUP BY U0."condition_id" LIMIT 1), 0) AS "normal_donation", (COALESCE((SELECT SUM(U0."amount") AS "amount_sum" FROM "wallets_wallettransaction" U0 WHERE U0."condition_id" = ("conditions"."id") GROUP BY U0."id" ORDER BY U0."id" DESC LIMIT 1), 0) + COALESCE((SELECT SUM(U0."amount") AS "amount_sum" FROM "transactions" U0 WHERE U0."condition_id" = ("conditions"."id") GROUP BY U0."condition_id" LIMIT 1), 0)) AS "total_donation" FROM "conditions" WHERE "conditions"."status" = Published ORDER BY "conditions"."id" DESC

注意:我在另一个视图中使用相同的注释,产生了相同的结果。

Abd*_*kat 9

您偶然发现了这样一个问题:组合多个聚合 [Django 文档]产生错误的结果,因为使用的是联接而不是子查询。

由于要对关系进行聚合,Django 会进行连接,并且对多个关系进行聚合,因此会有多个连接,结果当然是错误的。因此我们需要使用子查询进行注释:

from django.db.models import OuterRef, Subquery


class DonationQuerySet(QuerySet):
    ...
    
    def with_donations_stats(self):
        # Subquery for wallet donation
        # Added order_by because it appears you have some default ordering
        wallet_donation = WalletTransaction.objects.filter(
            condition=OuterRef('pk')
        ).order_by().values('condition').annotate(amount_sum=Sum('amount')).values('amount_sum')[:1]
        
        # Subquery for normal donation
        normal_donation = Transaction.objects.filter(
            condition=OuterRef('pk')
        ).values('condition').annotate(amount_sum=Sum('amount')).values('amount_sum')[:1]
        
        return self.annotate(
            wallet_donation=Coalesce(Subquery(wallet_donation), 0),
            normal_donation=Coalesce(Subquery(normal_donation), 0),
            total_donation=F('wallet_donation') + F('normal_donation')
        )
Run Code Online (Sandbox Code Playgroud)