【问题标题】:Django queryset : Calculate monthly averageDjango 查询集:计算月平均值
【发布时间】:2019-07-06 11:29:43
【问题描述】:

我有一个销售模型,我想计算按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)

因此,如果按月对销售额进行分组,那么每个月的销售额将变为(#transcations/#days in thatmonth)。现在,如果销售额按年份分组,则变为(当年的 # 次交易/# 天)

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

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

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

【问题讨论】:

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

标签: python django django-queryset


【解决方案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 中执行它可以在不损失显着性能(如果有的话)的情况下为您省去一些痛苦。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多