Django: 按日期(日、月、年)分组

我有一个这样的简单模型:

class Order(models.Model):
created = model.DateTimeField(auto_now_add=True)
total = models.IntegerField() # monetary value

我要按月分类输出:

  • 一个月有多少次销售(COUNT)
  • The combined value (SUM)

我不知道怎样才能最好地解决这个问题。我见过一些看起来相当吓人的额外选择查询,但我简单的头脑告诉我,我可能更好地只是迭代数字,从一个任意的开始年/月开始,直到我到达当前月份,抛出简单的查询过滤为该月。更多的数据库工作-更少的开发人员压力!

你觉得什么最合理?有没有什么好的方法可以让我快速地拉回一张数据表?还是说我的下流方法才是最好的主意?

我正在使用 Django 1.3。不确定他们最近是否给 GROUP_BY添加了一个更好的方法。

87216 次浏览

Here's my dirty method. It is dirty.

import datetime, decimal
from django.db.models import Count, Sum
from account.models import Order
d = []


# arbitrary starting dates
year = 2011
month = 12


cyear = datetime.date.today().year
cmonth = datetime.date.today().month


while year <= cyear:
while (year < cyear and month <= 12) or (year == cyear and month <= cmonth):
sales = Order.objects.filter(created__year=year, created__month=month).aggregate(Count('total'), Sum('total'))
d.append({
'year': year,
'month': month,
'sales': sales['total__count'] or 0,
'value': decimal.Decimal(sales['total__sum'] or 0),
})
month += 1
month = 1
year += 1

也许有更好的方法来循环年/月,但这并不是我真正关心的:)

姜戈1.10及以上

Django 文档将 extra列为 很快就被废弃了。(谢谢你指出@seddonym,@Lucas03)。我打开了一个 罚单,这是 Jarshhua 提供的解决方案。

from django.db.models.functions import TruncMonth
from django.db.models import Count


Sales.objects
.annotate(month=TruncMonth('created'))  # Truncate to month and add to select list
.values('month')                          # Group By month
.annotate(c=Count('id'))                  # Select the count of the grouping
.values('month', 'c')                     # (might be redundant, haven't tested) select month and count

老版本

from django.db import connection
from django.db.models import Sum, Count


truncate_date = connection.ops.date_trunc_sql('month', 'created')
qs = Order.objects.extra({'month':truncate_date})
report = qs.values('month').annotate(Sum('total'), Count('pk')).order_by('month')

编辑

  • 增加计数
  • 增加了 django > = 1.10的信息

另一种方法是使用 ExtractMonth。我在使用 TruncMonth 时遇到了麻烦,因为只返回了一个日期时间年值。例如,只返回2009年的月份。ExtratMonth 完美地解决了这个问题,可以像下面这样使用:

from django.db.models.functions import ExtractMonth
Sales.objects
.annotate(month=ExtractMonth('timestamp'))
.values('month')
.annotate(count=Count('id'))
.values('month', 'count')

按月:

 Order.objects.filter().extra({'month':"Extract(month from created)"}).values_list('month').annotate(Count('id'))

年份:

 Order.objects.filter().extra({'year':"Extract(year from created)"}).values_list('year').annotate(Count('id'))

白天:

 Order.objects.filter().extra({'day':"Extract(day from created)"}).values_list('day').annotate(Count('id'))

别忘了进口伯爵

from django.db.models import Count

对于 django < 1.10

Just a small addition to @tback answer: 对于 Django 1.10.6和 postgres,它不起作用,我在最后添加了 order _ by ()来修复它。

from django.db.models.functions import TruncMonth
Sales.objects
.annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
.values('month')                          # Group By month
.annotate(c=Count('id'))                  # Select the count of the grouping
.order_by()

下面是按任意时间段对数据进行分组的方法:

from django.db.models import F, Sum
from django.db.models.functions import Extract, Cast
period_length = 60*15 # 15 minutes


# Annotate each order with a "period"
qs = Order.objects.annotate(
timestamp=Cast(Extract('date', 'epoch'), models.IntegerField()),
period=(F('timestamp') / period_length) * period_length,
)


# Group orders by period & calculate sum of totals for each period
qs.values('period').annotate(total=Sum(field))
    metrics = {
'sales_sum': Sum('total'),
}
queryset = Order.objects.values('created__month')
.annotate(**metrics)
.order_by('created__month')


queryset是一个订单列表,每月一行,结合销售总额: sales_sum

@Django 2.1.7

我的数据库中有订单表。我打算在过去的3个月里每月清点订单

from itertools import groupby
from dateutil.relativedelta import relativedelta


date_range = datetime.now()-relativedelta(months=3)
aggs =Orders.objects.filter(created_at=date_range)\
.extra({'date_created':"date(created_at)"}).values('date_created')


for key , group in groupby(aggs):
print(key,len(list(group)))

Create _ at 是 datetime 字段。通过额外的函数所做的就是从日期时间值中获取日期。在使用 datetime 时,我们可能无法得到正确的计数,因为对象是在一天中的不同时间创建的。

For 循环将打印日期和计数数目