Джанго: группа по дате (день, месяц, год)
у меня есть простая модель такой:
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как скоро устаревший. (Спасибо, что указали на это @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 year. Например, возвращались только месяцы 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
Comments