Django считает людей по месяцам и годам

#python #mysql #django #datetime

#python #mysql #django #дата и время

Вопрос:

Я некоторое время занимался отладкой, но не могу получить правильный результат. В моем приложении django я расширил auth_user для добавления дополнительных полей:

 from django.contrib.auth.models import User
class Person(User):
    api_id = models.CharField(max_length=255)
    api_key = models.CharField(max_length=255)
  

Теперь я хочу подсчитать пользователей по годам и месяцам в соответствии с датой, когда они присоединились:

Person.objects.annotate(year=TruncYear("date_joined"), month=TruncMonth("date_joined")).values("year", "month").annotate(count=Count("pk"))

Однако все, что я получаю, это count = 1 для каждого пользователя в базе данных без группировки их по годам и месяцам.

Это фактический выполняемый запрос:

 SELECT CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-01-01 00:00:00') AS DATETIME) AS `year`, CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-%m-01 00:00:00') AS DATETIME) AS `month`, COUNT(`mt_api_app_person`.`user_ptr_id`) AS `count` FROM `mt_api_app_person` INNER JOIN `auth_user` ON (`mt_api_app_person`.`user_ptr_id` = `auth_user`.`id`) GROUP BY CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-01-01 00:00:00') AS DATETIME), CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-%m-01 00:00:00') AS DATETIME), `auth_user`.`date_joined` ORDER BY `auth_user`.`date_joined` DESC
  

Кажется, что все count = 1 происходит из GROUP BY [...] 'auth_user'.'date_joined' -за того, что группирует записи по целому datetime, а не только по месяцу и году.

Есть какие-либо подсказки о том, что здесь происходит? Если я выполняю тот же запрос, но с пользователем, я получаю то, что хочу.

 SELECT CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-%m-01 00:00:00') AS DATETIME) AS `month`, COUNT(`auth_user`.`id`) AS `count` FROM `auth_user` GROUP BY CAST(DATE_FORMAT(CONVERT_TZ(`auth_user`.`date_joined`, 'UTC', 'Europe/Madrid'), '%Y-%m-01 00:00:00') AS DATETIME) ORDER BY NULL
  

Ответ №1:

Используйте TruncMonth и TruncYear

 from django.db.models import Count
from django.db.models.functions import TruncMonth, TruncYear

Person.objects.annotate(month=TruncMonth('date_joined')).values('month').annotate(total=Count('pk'))
Person.objects.annotate(year=TruncYear('date_joined')).values('year').annotate(total=Count('pk'))
  

Комментарии:

1. Это не работает. Он возвращает count = 1 для каждого пользователя вместо группировки по месяцам и годам и возвращает количество, например, count = 251

Ответ №2:

Похоже, в вашем запросе опечатка. Вы используете TruncYear для месяца и года, поэтому вы получаете только одно значение, и это за год.

За месяц:

 Person.objects.annotate(month=TruncMonth("date_joined")).values("month").annotate(count=Count("pk"))
  

За год:

 Person.objects.annotate(year=TruncYear("date_joined")).values("year").annotate(count=Count("pk"))
  

Комментарии:

1. Вы правы, я допустил опечатку при копировании сюда запроса. Но я сделал это правильно на Django. Я исправил свой вопрос. Спасибо

Ответ №3:

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

 Person.objects.annotate(year=TruncYear('date_joined')).values('year').annotate(total=Count('pk')).order_by()