Вычисление данных по месяцам в django/python

Я получаю данные в виде начальной и конечной даты, теперь я хочу получить данные на основе месяцев между начальной и конечной датой. Как это можно сделать в python/django?

Я получаю дату в формате -

start_date = '2021-5-5'   #YYYY-MM-DD format
end_date = '2021-6-5'   

Требуемые результаты -

result = [{
'month' : 'may'   
'data' : data_for_may    # from date 5th of may to 31st of may
},
{
'month' : 'june'
'data' : data_for_june  # from date 1st of june to 5th of june
}
]

Сначала необходимо преобразовать строки в даты :

start_date = date.strptime(start_date , '%Y-%-m-%-d')
end_date = date.strptime(end_date , '%Y-%-m-%-d')

Тогда я думаю о чем-то подобном, но я не проверял это.

all_data = User.objects.filter(last_login__date__range=[start_date, end_date])
results = []
year = start_date.year
month = start_date.month
while year <= end_date.year and month <= end_date.month:
    results.append({
        'month': month,  # You could make the conversion from the number of the month to the name of the month
        'data': all_data.filter(last_login__date__month=month, last_login__date__year=year)
    })
    month += 1
    if month == 13:
        year += 1
        month = 0 

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

from itertools import groupby

data = User.objects.all().order_by('datetime')

result = []
for (year, month), data_per_month in groupby(data, key=lambda x: (x.datetime.year(), x.datetime.month())):
    result.append({
        'year': year,
        'month': month,
        'data': data_per_month   
    })

Это, вероятно, будет быстро и легко уместится в памяти с 10 000-ми объектами. Однако при выборке миллионов записей вам, возможно, придется пересмотреть свое мнение.

Я думаю, что вам лучше сделать:

from datetime import datetime
from django.db.models import Count
from django.contrib.auth.models import User

start_date = datetime.strptime('2021-5-5' , '%Y-%m-%d')
month_end_date = datetime.strptime('2021-6-5' , '%Y-%m-%d')

# SELECT year(last_login), month(last_login), count(*) 
# FROM auth_user 
# GROUP BY year(last_login), month(last_login)
# ORDER BY year(last_login), month(last_login)
qs = (User.objects.values('last_login__month', 'last_login__year')
                  .annotate(data=Count('*'))
                  .order_by('last_login__year', 'last_login__month'))
# WHERE last_login ...
qs = qs.filter(last_login__range=[start_date, month_end_date])

result = []
for item in qs:
    result.append({
        # get pretty name i.e "January"
        'month': datetime(1900, item['last_login__month'] , 1).strftime('%B'),
        'data': item['data']
    })

result  # [{'month': 'May', 'data': 81}, {'month': 'June', 'data': 15}])

Почему я считаю, что это лучше? (по сравнению с другими представленными ответами)

У вас будет только 1 запись В МЕСЯЦ В ГОД, легко количественно оценить/прогнозировать, лучше по производительности.


Кстати, я написал для вас тесты ;)

https://gist.github.com/kingbuzzman/0197da03c52ae9a798c99d0cf58c758c#file-month_data-py-L82-L133

В качестве комментария внутри gist я предоставляю примеры того, как протестировать его с помощью docker

Вернуться на верх