Группировка по неделям и отключение «отсутствующих» недель

В моей модели Django у меня очень простая модель, которая представляет собой одно событие события (например, появление сервера):

class EventOccurrence: event = models.ForeignKey(Event) time = models.DateTimeField() 

Моя конечная цель – создать таблицу или график, который показывает, сколько раз произошло событие за последние n недель.

Поэтому мой вопрос состоит из двух частей:

  • Как я могу group_by неделю поля time ?
  • Как я могу «вычеркнуть» результат этой group_by чтобы добавить нулевое значение для любых отсутствующих недель?

Например, для второй части я хотел бы преобразовать результат следующим образом:

 | week | count | | week | count | | 2 | 3 | | 2 | 3 | | 3 | 5 | —— becomes —> | 3 | 5 | | 5 | 1 | | 4 | 0 | | 5 | 1 | 

Каков наилучший способ сделать это в Django? Общие решения Python также в порядке.

DateField Django, а также datetime не поддерживает атрибут недели. Чтобы получить все в одном запросе, вам нужно сделать:

 from django.db import connection cursor = connection.cursor() cursor.execute(" SELECT WEEK(`time`) AS 'week', COUNT(*) AS 'count' FROM %s GROUP BY WEEK(`time`) ORDER BY WEEK(`time`)" % EventOccurrence._meta.db_table, []) data = [] results = cursor.fetchall() for i, row in enumerate(results[:-1]): data.append(row) week = row[0] + 1 next_week = results[i+1][0] while week < next_week: data.append( (week, 0) ) week += 1 data.append( results[-1] ) print data 

После копания django query api doc, я не нашел способ сделать запрос через систему ORM django. Курсор является обходным путем, если ваш бренд базы данных – это MySQL:

 from django.db import connection, transaction cursor = connection.cursor() cursor.execute(""" select week(time) as `week`, count(*) as `count` from EventOccurrence group by week(time) order by 1;""") myData = dictfetchall(cursor) 

Это, на мой взгляд, лучшее решение для повышения производительности. Но обратите внимание, что это не пропустит недели.

EDITED Независимое решение для бренда базы данных с помощью python (меньше производительности)

Если вы ищете код независимой базы данных, тогда вы должны принимать даты с каждым днем ​​и суммировать их через python. Если это ваш код, код может выглядеть так:

 #get all weeks: import datetime weeks = set() d7 = datetime.timedelta( days = 7) iterDay = datetime.date(2012,1,1) while iterDay <= datetime.now(): weeks.add( iterDay.isocalendar()[1] ) iterDay += d7 #get all events allEvents = EventOccurrence.objects.value_list( 'time', flat=True ) #aggregate event by week result = dict() for w in weeks: result.setdefault( w ,0) for e in allEvents: result[ e.isocalendar()[1] ] += 1 

(Отказ от ответственности: не проверен)

Поскольку я должен запросить несколько таблиц, присоединив их, я использую db view для решения этих требований.

 CREATE VIEW my_view AS SELECT *, // <-- other fields goes here YEAR(time_field) as year, WEEK(time_field) as week FROM my_table; 

и модель как:

 from django.db import models class MyView(models.Model): # other fields goes here year = models.IntegerField() week = models.IntegerField() class Meta: managed = False db_table = 'my_view' def query(): rows = MyView.objects.filter(week__range=[2, 5]) # to handle the rows 

после получения строк из этого представления db используйте путь @danihp для заполнения 0 для «дыры» недель / месяцев.

ПРИМЕЧАНИЕ. Это проверяется только на бэкэнд MySQL, я не уверен, что это нормально для MS SQL Server или другого.