Python Django queryset:计算月平均值

Python Django queryset:计算月平均值,python,django,django-queryset,Python,Django,Django Queryset,我有一个销售模型,我想在按月份、周、年分组时计算(交易数量)/(天数) class SaleItem(models.Model): id = models.UUIDField(default=uuid4, primary_key=True) bill = models.ForeignKey() item = models.ForeignKey('item') quantity = models.PositiveSmallIntegerField() pri

我有一个销售模型,我想在按
月份
分组时计算(交易数量)/(天数)

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)
因此,如果销售按月份分组,则每个月的销售额为(#Transactions/#天)。现在,如果销售额按年份分组,则该年为(#Transactions/#天)

目前我可以得到交易的数量

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

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

用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中使用它可以在不损失显著性能(如果有的话)的情况下为您节省一些痛苦。

在Python/Django方面这样做可能是个好主意,您可以使用
calendar.month\u range
获取给定月份的天数。我担心,与在db级别处理它相比,这会降低性能。为什么?这只是一个线性运算。由于反序列化了我们从数据库接收到的数据,如果它如此重要,我真的会感到惊讶。是的,应在db端计算骨料等,但这只是骨料的“后处理”。数据库本身处理数据的效率并不比Python/Django更高,它处理数据聚合、过滤等的效率更高。我明白了。那我就试试那条路线。