Django queryset:计算每月平均值

时间:2019-02-12 18:29:07

标签: python django django-queryset

我有一个销售模型,当按monthweekyear分组时,我想计算(交易数)/(天数)。

class SaleItem(models.Model):
    id = models.UUIDField(default=uuid4, primary_key=True)
    bill = models.ForeignKey()
    item = models.ForeignKey('item')
    quantity = models.PositiveSmallIntegerField()
    price = models.DecimalField(max_digits=13, decimal_places=3, default=0)

因此,如果按月对销售进行分组,则每个月将变成(该月的交易次数/天数)。现在,如果按年份将销售额分组,则变为(该年的交易次数/天数)

当前我可以获得交易数量

aggregate = 'month' # parameter
# get number of transactions
SaleItem.objects.annotate(date=Trunc('bill__date', aggregate)).values('date').annotate(sales=Count('bill', distinct=True))

但是我如何将每个计数除以该组中的天数呢?

1 个答案:

答案 0 :(得分:1)

可以在SQL中执行(甚至没有那么困难)。但是,获取一个月中的天数是RDBMS特定的,并且没有通用的Django数据库功能可以使您免受各种SQL实现的影响。

Django使使用SQL函数包装自己的函数非常容易。例如,对于SQLite,您可以定义

class DaysInMonth(Func):
    output_field = IntegerField()
    def as_sqlite(self, compiler, connection):
        return super().as_sql(
            compiler, 
            connection,
            function='strftime',
            template='''
            %(function)s("%%%%d", 
            %(expressions)s, 
            "start of month", 
            "+1 month", 
            "-1 day")
            ''',
        )

然后,您可以使用DaysInMonth()将计数除以天数:

qs = (
    SaleItem.objects
        .annotate(date=Trunc('bill__date', aggregate))
        .values('date')
        .annotate(
            sales = Count('bill', distinct=True),
            sales_per_day = F('sales') / DaysInMonth('date')
        )
)

如果四舍五入的整数不够,并且您需要十进制结果,则这是另一个跳过的障碍:

sales_per_day=ExpressionWrapper(
    Cast('sales', FloatField()) / DaysInMonth(F('date')), 
    DecimalField()
)

如果,天哪,您想在数据库中而不是在模板中四舍五入,则需要另一个自定义函数:

class Round(Func):
  function = 'ROUND'
  output_field = FloatField()
  arity = 2

sales_per_day=Round(
    Cast('sales', FloatField()) / DaysInMonth(F('date')), 
    2 # decimal precision
)

因此Django确实非常灵活,但是正如Willem所说,用Python进行操作会为您省去一些麻烦,而又不会损失任何性能(如果有的话)。