Агрегация Джанго по диапазону дат
Я скрывался и учусь здесь некоторое время. Теперь у меня есть проблема, которая почему-то не может найти легкое решение. Чтобы узнать django, я создаю приложение, которое в основном отслеживает забронированные предметы. Я хотел бы показать, сколько дней в месяце для выбранного года было забронировано одно изделие.
у меня есть следующие модели:
Asset(Model)
BookedAsset(Model):
asset = models.ForeignKey(Asset)
startdate = models.DateField()
enddate = models.DateField()
Таким образом, имея следующие записи:
asset 1, 2010-02-11, 2010-02-13
asset 2, 2010-03-12, 2010-03-14
asset 1, 2010-04-30, 2010-05-01
Я хотел бы получить следующее:
asset 1 asset 2
------- -------
Jan = 0 Jan = 0
Feb = 2 Feb = 0
Mar = 0 Mar = 2
Apr = 1 Apr = 0
May = 1 May = 0
Jun = 0 Jun = 0
Jul = 0 Jul = 0
Aug = 0 Aug = 0
Sep = 0 Sep = 0
Oct = 0 Oct = 0
Nov = 0 Nov = 0
Dec = 0 Dec = 0
Я знаю, что мне нужно сначала узнать количество дней в диапазоне дат (и отслеживать, выпадают ли они из текущего месяца и в следующий месяц), а затем составить общее количество дней. Я просто застрял на том, как сделать это элегантно в Джанго.
Любая помощь (или подсказка в правильном направлении) очень ценится.
4 ответа
В конце я выбрал собственный запрос в качестве решения моего вопроса:
cursor = connection.cursor()
cursor.execute("""select to_char(allmonths.yeardate::date, 'YYYY/MM/DD') as monthdate,
COUNT("day") as bookings
from (
select distinct date_trunc('month', (date %s - offs)) as yeardate
from generate_series(0,365,28) as offs
) as allmonths
left join bookings_bookedassetday
on EXTRACT(MONTH from "day")=EXTRACT(MONTH from yeardate)
and asset_id=%s and EXTRACT(YEAR from "day") = %s
group by allmonths.yeardate
order by allmonths.yeardate asc""", [year+'-12-31', id, year])
query = cursor.fetchall()
Возможно, это не самый лучший способ сделать это, но я нашел это гораздо проще, чем понять это с помощью чистого django:|
Если у кого-то есть лучшая альтернатива, я весь в ушах:)
Я не могу придумать способ сделать это с вашей моделью.
Это довольно сложное требование, и как бы вы его ни решали, вероятно, потребуется много пользовательского SQL. Я думаю, что для начала вам может понадобиться изменить свою структуру, чтобы BookedAssetDay
таблица, которая представляет каждый день отдельно для бронирования.
class BookedAsset(models.Model):
asset = models.ForeignKey(Asset)
day = models.DateField()
Тогда запрос выглядит примерно так:
BookedAsset.objects.extra(
select={'month': 'MONTH(day)'}
).values('asset', 'month').annotate(Count('bookedasset__month'))
Есть другое решение, которое делает это через представление базы данных и отображает это представление в модель django, используя опцию мета-класса managed = False
Подробные объяснения здесь: http://anthony-tresontani.github.com/Django/2012/09/12/wka-django-orm-limitations/
Я бы не стал использовать ни один из приведенных выше ответов (даже если бы мне нравилось пробивать некоторые пользовательские SQL, но я не делал этого по крайней мере в течение 5 лет), и ваша модель достаточно проста, чтобы ее можно было использовать в реляционной парадигме.
Ответ, который вы ищете, - это функция annotate() в функции агрегирования django: http://docs.djangoproject.com/en/dev/topics/db/aggregation/ Единственное, что вам нужно знать, это как месяц для даты, который объясняется в документации набора запросов: http://docs.djangoproject.com/en/dev/ref/models/querysets/
Грубый пример, чтобы получить его для одного актива:
BookedAsset.objects.filter(asset=asset).annotate(month_count=Count('startdate__month')).order_by('startdate__month')
(очевидно, что это неправильно, но я не мог отказаться от воссоздания вашей структуры, чтобы дать вам точное правильное утверждение, немного покопаться и прочитать документацию)
Вероятно, вы могли бы даже при том, что это за один раз для всех активов, используя синтаксис соединения в тех же документах.