запрос значений на основе даты с помощью Django ORM
-
21-09-2019 - |
Вопрос
У меня есть куча объектов, которые имеют значение и поле даты:
obj1 = Obj(date='2009-8-20', value=10)
obj2 = Obj(date='2009-8-21', value=15)
obj3 = Obj(date='2009-8-23', value=8)
Я хочу, чтобы это вернули:
[10, 15, 0, 8]
или, что еще лучше, совокупность общего количества до этого момента:
[10, 25, 25, 33]
Лучше всего было бы получить эти данные непосредственно из базы данных, но в противном случае я могу довольно легко выполнить суммирование с помощью forloop.
Я использую ORM от Django, а также Postgres
Редактировать:
Просто хочу отметить, что мой пример охватывает всего несколько дней, но на практике у меня есть сотни объектов, охватывающих пару десятилетий...То, что я пытаюсь сделать, это создать линейный график, показывающий, как сумма всех моих объектов росла с течением времени (очень долгое время)
Решение
Этот не тестируется, так как настраивать таблицу Django для тестирования слишком сложно:
from datetime import date, timedelta
# http://www.ianlewis.org/en/python-date-range-iterator
def datetimeRange(from_date, to_date=None):
while to_date is None or from_date <= to_date:
yield from_date
from_date = from_date + timedelta(days = 1)
start = date(2009, 8, 20)
end = date(2009, 8, 23)
objects = Obj.objects.filter(date__gte=start)
objects = objects.filter(date__lte=end)
results = {}
for o in objects:
results[o.date] = o.value
return [results.get(day, 0) for day in datetimeRange(start, end)]
Это позволяет избежать выполнения отдельного запроса на каждый день.
Другие советы
result_list = []
for day in range(20,24):
result = Obj.objects.get(date=datetime(2009, 08, day))
if result:
result_list.append(result.value)
else:
result_list.append(0)
return result_list
Если у вас есть более одного объекта на дату, вам нужно будет проверить len(obj) и выполнить итерацию по ним, если их больше 1.
Если вы перебираете Obj.objects.get 100 раз, вы выполняете 100 SQL-запросов.Obj.objects.filter вернет результаты в одном SQL-запросе, но вы также выбираете все поля модели.Правильный способ сделать это - использовать Obj.objects.values_list , который сделает это с помощью одного запроса и выберет только поле "значения".
start_date = date(2009, 8, 20)
end_date = date(2009, 8, 23)
objects = Obj.objects.filter(date__range=(start_date,end_date))
# values_list and 'value' aren't related. 'value' should be whatever field you're querying
val_list = objects.values_list('value',flat=True)
# val_list = [10, 15, 8]
Чтобы выполнить запуск агрегата val_list, вы можете сделать это (не уверен, что это самый питонический способ)
for i in xrange(len(val_list)):
if i > 0:
val_list[i] = val_list[i] + val_list[i-1]
# val_list = [10,25,33]
Редактировать:Если вам нужно учесть пропущенные дни, ответ @Glenn Maynard на самом деле довольно хорош, хотя я предпочитаю синтаксис dict():
objects = Obj.objects.filter(date__range=(start_date,end_date)).values('date','value')
val_dict = dict((obj['date'],obj['value']) for obj in objects)
# I'm stealing datetimeRange from @Glenn Maynard
val_list = [val_dict.get(day, 0) for day in datetimeRange(start_date, end_date)]
# val_list = [10,15,0,8]