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 查询集产生的 Store
s,将有一个名为 .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
通过过滤,您将忽略当天没有 Sale
的 Store
。您可以通过稍微更改条件来包含这些内容:
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>
)
)
我有这些型号:
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 查询集产生的 Store
s,将有一个名为 .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
通过过滤,您将忽略当天没有 Sale
的 Store
。您可以通过稍微更改条件来包含这些内容:
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>
)
)