Джанго: группа по дате (день, месяц, год)

у меня есть простая модель такой:

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

и я хочу вывести разбивку по месяцам:

  • сколько было продаж в месяц (COUNT)
  • общая стоимость (SUM)

Я не уверен, что лучший способ атаковать это. Я видел некоторые довольно страшные дополнительные запросы, но мой простой ум говорит мне, что мне лучше всего просто повторять числа, начиная с произвольного начала год / месяц и подсчет, пока я не достигну текущего месяца, выбрасывая простую фильтрацию запросов за этот месяц. Больше работы с базой данных-меньше стресса для разработчиков!

что имеет для вас наибольший смысл? Есть ли хороший способ вернуть быструю таблицу данных? Или мой грязный метод, вероятно, лучшая идея?

Я использую Django 1.3. Не уверен, что они добавили более приятный способ GROUP_BY недавно.

5 ответов


Django 1.10 и выше

списки документацию Django extra as скоро устаревший. (Спасибо, что указали на это @seddonym, @Lucas03). Я открылавиабилет и это решение, которое предоставил джаршва.

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

просто небольшое дополнение к ответу @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()

другой подход-использовать ExtractMonth. Я столкнулся проблемы с использованием TruncMonth за счет только одного типа datetime значение года возвращается. Например, были возвращены только месяцы 2009 года. ExtractMonth исправил эту проблему отлично и может быть использован, как показано ниже:

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

вот мой грязный метод. Это грязно.

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

там вполне может быть лучший способ зацикливания лет/месяцев, но это не совсем то, что меня волнует :)


месяц:

 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 *

для django