Django注解,访问当前行遍历外键

Django annotate, access current row to traverse foreign keys

我有这些型号:

class Sale(models.Model):
    date = models.DateField()
    profit = models.FloatField()
    expenses = models.FloatField()
    seller = models.ForeignKey(Seller)

class Seller(models.Model):
    name = models.CharField()
    store = models.ForeignKey(Store)


class Store(models.Model):
    name = models.CharField()
    country = models.ForeignKey(Country)

给定 ids = [a list of seller ids...],我需要这样的 table:

Store 1 -> sum of profits - sum of expenses of all sellers in ids and in store 1
Store 2 -> sum of profits - sum of expenses of all sellers in ids and in store 2
...

我可以这样获取所有商店:

sellers = Seller.objects.filter(id__in=ids)
q = Store.objects.filter(id__in=sellers.values("store_id"))

在这一点上,我将迭代 q 但这将违背拥有 ORM 的意义。

替代方案是使用.annotate(),但我如何告诉.annotate获取商店N中所有卖家的所有利润总和?

我觉得你把事情搞得太复杂了。您可以用以下方式注释您的 Store

from django.db.models import Sum

Store.objects.annotate(
    <b>balance=Sum('seller__sale__profit') - Sum('seller__sale__expenses')</b>
)

或者用一个聚合,和两个F-expressions [Django-doc]:

from django.db.models import F, Sum

Store.objects.annotate(
    <b>balance=Sum(F('seller__sale__profit') - F('seller__sale__expenses'))</b>
)

所有从 this 查询集产生的 Stores,将有一个名为 .balance 的额外属性,其中包含 [=15= Sale 中与 Seller 相关的 Store 减去 Sale 中与 [=] 相关的 expenses 17=] 与 Store 相关。如果没有这样的销售额,那么.balance就是None,总和也可以为零(如果所有利润总和等于所有费用)。

您可以通过仅考虑特定日期的销售额来计算 Store 的余额:

from datetime import date
from django.db.models import F, Sum

Store.objects.filter(
    <b>seller__sale__date=date(2019, 9, 1)</b>
).annotate(
    balance=Sum(F('seller__sale__profit') - F('seller__sale__expenses'))
)

这将生成如下所示的查询:

SELECT store.id, store.name, store.country_id
       SUM((sale.profit - sale.expenses)) AS balance
FROM store
INNER JOIN seller ON store.id = seller.store_id
INNER JOIN sale ON seller.id = sale.seller_id
WHERE sale.date = 2019-09-01
GROUP BY store.id

通过过滤,您将忽略当天没有 SaleStore。您可以通过稍微更改条件来包含这些内容:

from datetime import date
from django.db.models import F, Sum, <b>Q</b>

Store.objects.annotate(
    balance=Sum(
        F('seller__sale__profit') - F('seller__sale__expenses'),
        <b>filter=Q(seller__sales__date=date(2019, 9, 1))</b>
    )
)