2013-05-30 3 views
6

У меня есть приложение django, которое делает некоторые записи. Моя модель выглядит следующим образом:Django совокупное количество записей в день

class MessageLog(models.Model): 
    logtime = models.DateTimeField(auto_now_add=True) 
    user = models.CharField(max_length=50) 
    message = models.CharField(max_length=512) 

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

for i in range(1, 8): 
    MessageLog.objects.filter(logtime__week_day=i).count() 

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

for i in range(1, 8): 
    MessageLog.objects.filter(logtime__week_day=i).annotate(num_msgs=Count('id')).aggregate(Avg('num_msgs')) 

По какой-то причине это возвращает 1.0 за каждый день. Я посмотрел на SQL это генерирующее и это:

SELECT AVG(num_msgs) FROM (
SELECT 
`myapp_messagelog`.`id` AS `id`, `myapp_messagelog`.`logtime` AS `logtime`, 
`myapp_messagelog`.`user` AS `user`, `myapp_messagelog`.`message` AS `message`, 
COUNT(`myapp_messagelog`.`id`) AS `num_msgs` 
FROM `myapp_messagelog` 
WHERE DAYOFWEEK(`myapp_messagelog`.`logtime`) = 1 
GROUP BY `myapp_messagelog`.`id` ORDER BY NULL 
) subquery 

Я думаю, что проблема может приходить с GROUP BY ид, но я не совсем уверен. У кого-нибудь есть идеи или предложения? Заранее спасибо!

ответ

9

Причина, по которой ваш указанный запрос всегда дает 1, состоит в том, что вы не группируете по дате. В принципе, вы попросили базу данных взять строки MessageLog, которые попадают в данный день недели. Для каждой такой строки подсчитайте количество идентификаторов (всегда 1). Затем возьмите среднее значение всех этих подсчетов, что, конечно, также 1.

нормально, вам нужно будет использовать пункт values группировать MessageLog строки перед вашими annotate и aggregate частей. Однако, поскольку ваше поле logtime является датой, а не просто датой, я не уверен, что вы можете выразить это напрямую с ORM Django. Вы можете определенно сделать это с помощью предложения extra, как показано на рисунке here. Или, если вам кажется, вы можете объявить представление в своем SQL с такой же суммарной и средней математикой, сколько вам понравилось, и объявить неуправляемую модель для нее, а затем просто использовать ORM в обычном режиме.

Таким образом, поле extra работает, чтобы получить общее количество записей за фактический день, но не обрабатывает агрегирование среднего значения вычисленной аннотации. Я думаю, что это может быть достаточно абстрагировано от модели, что вам придется использовать необработанный SQL-запрос, или, по крайней мере, я не могу найти ничего, что заставило бы его работать за один звонок.

Тем не менее, вы уже знаете, как вы можете получить общее количество записей в будний день в простом запросе, как показано в вашем вопросе.

И этот запрос будет сказать вам, сколько различных даты записи есть на будний день:

MessageLog.objects.filter(logtime__week_day=i).dates('logtime', day').count() 

Таким образом, вы могли бы сделать осреднение математик в Python вместо этого, что может быть проще, чем пытаться получить право SQL ,

С другой стороны, этот запрос получит Вас необработанное количество сообщений для всех дней недели в одном запросе, а не для цикла:

MessageLog.objects.extra({'weekday': "dayofweek(logtime)"}).values('weekday').annotate(Count('id')) 

Но я не был в состоянии получить хороший запрос, чтобы дать вам количество отдельных дат для каждого буднего дня, аннотированное к этому - даты запросов не могут обрабатывать аннотированные вызовы, а аннотация по значению extra тоже не работает.

Это было удивительно сложно, учитывая, что это не так сложно выражение SQL.

+0

Спасибо за объяснение! Это имеет большой смысл. Я попытался использовать дополнительное предложение, однако теперь у меня возникает ошибка SQL. Я изменил свой запрос: MessageLog.objects.filter (logtime__week_day = i) .extra ({'date_logged': "date (logtime)"}). Values ​​('date_logged') .annotate (num_msgs = Count ('id')) .aggregate (Avg ('num_msgs')) И ошибка, которую я получаю, это «У вас есть ошибка в синтаксисе SQL, проверьте руководство, соответствующее версии вашего сервера MySQL, для правильного синтаксиса используйте «FROM (SELECT (date (logtime)) AS' date_logged', COUNT ('myapp_messagelog'.'id 'в строке 1") – bb89

+0

Да, это не совсем работает, не так ли? Сложное предложение отбрасывает его - это работает с аннотатом. Вы можете подсчитать общее количество сообщений в день недели несколькими разными способами, но я не могу найти способ сделать Django ORM для этого одним вызовом. что я придумал h в мой ответ. –

+0

На самом деле я создал создание (на самом деле нужно 2 для mysql), а затем неуправляемую модель, как вы предполагали, и которая отлично работала. Я добавлю еще одно сообщение, объясняющее, что я сделал позже для тех, кого это может заинтересовать. Еще раз спасибо за вашу помощь! – bb89

2

Я делаю что-то подобное с полем datetime, но аннотация для дополнительных значений работает для меня. У меня есть модель Record с полем datetime «created_at» и поле «my_value», для которого я хочу получить среднее значение.

from django.db.models import Avg 

qs = Record.objects.extra({'created_day':"date(created_at)"}).\ 
    values('created_day').\ 
    annotate(count=Avg('my_value) 

Вышеуказанные будут группироваться в день значения даты и времени в поле «created_at».

Смежные вопросы