110

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

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

我想逐月输出:

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

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

什么对你最有意义?有什么好方法可以拉回一个快速的数据表吗?或者我的肮脏方法可能是最好的主意?

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

4

8 回答 8

259

Django 1.10 及更高版本

Django 文档列表很快就会extra弃用。(感谢您指出@seddonym,@Lucas03)。我开了一张票,这是 jarshwah 提供的解决方案。

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 的信息
于 2012-01-05T17:03:43.240 回答
50

只是对@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()
于 2017-05-08T20:37:07.860 回答
13

另一种方法是使用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')  
于 2016-12-01T21:54:48.620 回答
4
    metrics = {
        'sales_sum': Sum('total'),
    }
    queryset = Order.objects.values('created__month')
                               .annotate(**metrics)
                               .order_by('created__month')

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

@Django 2.1.7

于 2019-07-31T03:16:23.620 回答
1

这是我的肮脏方法。这东西好脏。

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

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

于 2012-01-05T16:59:35.270 回答
1

以下是如何按任意时间段对数据进行分组:

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))
于 2019-06-05T19:25:24.337 回答
0

按月份:

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

不要忘记导入 Count

from django.db.models import Count

对于 django < 1.10

于 2016-12-28T07:20:27.830 回答
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 是日期时间字段。通过额外的功能,从日期时间值中获取日期。使用 datetime 时,我们可能无法正确计算,因为对象是在一天中的不同时间创建的。

for 循环将打印日期和计数

于 2020-09-17T11:34:51.170 回答