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

时间:2012-01-05 16:30:08

标签: django django-aggregation

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

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

我想逐月输出:

  • 一个月内有多少销售额(COUNT
  • 组合值(SUM

我不确定攻击它的最佳方法是什么。我已经看到了一些相当可怕的额外选择查询,但我简单的想法告诉我,我可能会更好的只是迭代数字,从任意的开始年/月开始计算直到我到达当前月份,抛弃简单查询过滤该月份。更多数据库工作 - 减少开发人员压力!

对你来说最有意义的是什么?有没有一种很好的方法可以撤回快速的数据表?或者我的脏方法可能是最好的主意吗?

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

8 个答案:

答案 0 :(得分:191)

Django 1.10及以上

Django文档列出了extra,因为很快就弃用了。 (感谢你指出@seddonym,@ Lucas03)。我打开了一个ticket,这是jarshwah提供的解决方案。

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

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
    .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
  • 的信息

答案 1 :(得分:16)

只是@tback回答的一小部分内容: 用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()

答案 2 :(得分:5)

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

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

答案 3 :(得分:2)

    metrics = {
        'sales_sum': Sum('total'),
    }
    queryset = Order.objects.values('created__month')
                               .annotate(**metrics)
                               .order_by('created__month')

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

@Django 2.1.7

答案 4 :(得分:0)

这是我的脏方法。它很脏。

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

可能有更好的循环年/月的方法,但这并不是我真正关心的事情:)

答案 5 :(得分:0)

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

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))

答案 6 :(得分:0)

我的数据库中有订单表。我要计算最近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)))

created_at是日期时间字段。通过额外的功能,完成的工作是从日期时间值中获取日期。使用日期时间时,由于对象是在一天中的不同时间创建的,因此我们可能无法正确计数。

for循环将打印日期和计数

答案 7 :(得分:-2)

按月:

 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&lt; 1.10