Subquery

合計値のアノテート

サブクエリの合計値:

  • 合計対象レコードを OuterRefで条件指定してして絞り込む
  • values() で ユニークフィールドで GROUP BY する
  • 対象フィールドの合計値をanotate する
  • アノテートしたフィールドを values() で ValueList にしてSubqueryで返す(1件のはず)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def verify():
    from django.db.models import OuterRef, Subquery, F
    from django.db.models.functions import Coalesce
    from base.models.utils import aggr_sum_decimal as D
    from decimal import Decimal

    ref_query = dict(
        book__company_code=OuterRef("company_code"),
        book__processing_month=OuterRef("processing_month"),
        account_code=OuterRef("account_code"),
        account_item_code=OuterRef("account_item_code"),
        advances_code=OuterRef("outsource_code"),
        advances_branch_code=OuterRef("outsource_branch_code"),
    )

    def bookitem_sum(bookitem):
        qs = (
            bookitem.objects.filter(**ref_query)
            .values(*ref_query.keys())
            .annotate(total=D("amount"))
            .values("total")
        )

        return Subquery(qs)

    qs = models.CostOutsource.objects.annotate(
        sum_debit=Coalesce(bookitem_sum(BookDebit), Decimal("0")),
        sum_credit=Coalesce(bookitem_sum(BookCredit), Decimal("0")),
    ).exclude(debit_amount=F("sum_debit"), credit_amount=F("sum_credit"))

Bing

Djangoのクエリセットで、別のテーブルのフィールドの合計値をSubqueryを使ってannotateすることができます¹。以下は、例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.db.models import Subquery, Sum
from django.db.models.functions import Coalesce

subquery = Subquery(
    AnotherModel.objects.filter(
        id=OuterModel.objects.filter(id=OuterModelId).values('another_model_id')
    ).annotate(
        total=Coalesce(Sum('field_name'), 0)
    ).values('total')
)

result = OuterModel.objects.annotate(
    total=subquery
).values('total')

この例では、AnotherModelという別のモデルから、OuterModelというモデルに対して、Subqueryを使ってannotateしています。この例を参考にして、あなたのコードに合わせて書き換えてみてください。

¹: Django:クエリを使用して列の値の合計を計算します - QA Stack

ソース: Bing との会話 2023/5/17