我正在尝试创建一个模型管理器查询,该查询返回在给定日期范围内多个余额类型(DATA和AIRTIME)按天分组的结果。当sim使用数据时,Balance历史表会一直更新,但是对于报告我们只想每天显示一个余额
模型很简单:
class Sim(TimeStampedModel):
number = models.CharField()
class SimBalanceHistory(TimeStampedModel):
balance_type = models.CharField(choices=BALANCE_TYPES, max_length=10)
amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
sim = models.ForeignKey(Sim, related_name='balance_histories')
来自SimBalanceHistory表的一些示例数据:
ID BALANCE_TYPE AMOUNT SIM_ID CREATED MODFIED
1603 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1604 DATA 36.75 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1703 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1704 DATA 36.74 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1803 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1804 DATA 36.73 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1973 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
1974 DATA 36.72 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2059 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2060 DATA 36.72 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2135 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2136 DATA 36.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2229 AIRTIME 3.71 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159 +02:00
2230 DATA 36.70 348 2016-11-17 11:13:42.498180 +02:00 2016-11-17 11:13:43.543159
440026 DATA 34.26 348 2016-11-18 23:34:36.976777 +02:00 2016-11-18 23:34:36.976836 +02:00
440885 AIRTIME 3.71 348 2016-11-18 23:57:57.448809 +02:00 2016-11-18 23:57:57.448878 +02:00
440889 DATA 34.25 348 2016-11-18 23:57:58.854901 +02:00 2016-11-18 23:57:58.854959 +02:00
443590 AIRTIME 3.71 348 2016-11-19 00:35:07.598679 +02:00 2016-11-19 00:35:07.598755 +02:00
443593 DATA 34.24 348 2016-11-19 00:35:08.991217 +02:00 2016-11-19 00:35:08.991266
目前查询如下:
def daily_balances(self, start_date, end_date):
return self.filter(
created__range=[start_date, end_date]
).dates(
'created',
'day',
order='DESC'
).order_by(
'-created'
).distinct(
'created', 'balance_type'
).values(
'created',
'amount',
'balance_type'
)
按天限制,但每个balance_type
返回一行{'balance_type': 'AIRTIME', 'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'amount': Decimal('5.00')}
{'balance_type': 'DATA', 'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'amount': Decimal('12.00')}
我想要的是在查询集的结果中是这样的(每天1条记录,其中包含通话时间金额和数据量的值:
{'created': datetime.datetime(2016, 11, 22, 0, 0, tzinfo=<UTC>), 'data_amount': Decimal('5.00'), 'airtime_amount': Decimal('12.00')}
{'created': datetime.datetime(2016, 11, 21, 0, 0, tzinfo=<UTC>), 'data_amount': Decimal('6.00'), 'airtime_amount': Decimal('14.00')}
答案 0 :(得分:1)
这是我会尝试的。
首先,在每一行中重命名具有特定于其类型的名称的金额:data_amount和airtime_amount。 然后,按照&#39;创建的&#39;对行进行分组。同时做每笔金额的总和。
所以,我认为你可以做点像
from django.db.models import F, Sum
def daily_balances(self, start_date, end_date):
airtime_records = SimBalanceHistory.objects.filter(
created__range=[start_date, end_date],
balance_type='AIRTIME'
).annotate(airtime_amount=F('amount'))
data_records = SimBalanceHistory.objects.filter(
created__range=[start_date, end_date],
balance_type='DATA'
).annotate(data_amount=F('amount'))
return (airtime_records | data_records).dates(
'created',
'day',
order='DESC'
).order_by(
'-created'
).values('created').annotate(
Sum('data_amount'),
Sum('airtime_amount'),
)
该代码会产生多个查询,但我无法想到只有一个解决方案。希望有人会发布一个更好的答案,但也许这可以帮助你。
参考文献
答案 1 :(得分:1)
我认为您现有的查询已经相当不错,但如果您确实希望每天有一行同时使用这两个余额,则可以使用conditional aggregates:
from django.db.models import IntegerField, F, Sum, When
SimBalanceHistory.objects\
.filter(created__range=[start_date, end_date])\
.dates('created', 'day', order='DESC')\
.values('created')\
.annotate(airtime_amount=Sum(Case(When(balance_type='AIRTIME', then=F('amount')), output_field=DecimalField())),
data_amount=Sum(Case(When(balance_type='DATA', then=F('amount')), output_field=DecimalField())))