Django Query将每行的值聚合为单个结果

时间:2016-11-22 09:45:53

标签: django postgresql

我正在尝试创建一个模型管理器查询,该查询返回在给定日期范围内多个余额类型(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')}

2 个答案:

答案 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())))