В Django, как построить набор запросов для фильтрации по времени на отрезках времени?
Я использую Python 3.9 и Django 3.2. У меня есть такая модель цены
class Price(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
price = models.FloatField(null=False)
created = models.DateTimeField(null=False, default=datetime.now)
Если я хочу получить цену за час за последние 24 часа, я могу запустить метод, подобный этому
def _get_prices_per_time_slice(self, last_hours=24):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
Но, допустим, я хочу получать цену каждые последние X часов.
def _get_prices_per_time_slice(self, last_hours=24, time_slice_in_hours=4):
Итак, если текущее время - полночь (и ноль секунд и минут), я хочу получить цены для полуночи, 8 вечера, 4 вечера, полудня, 8 утра и 4 утра. Как мне добавить фильтр для отбора цен каждые X часов?
Функция range в python помогает задать значение шага приращения
СИНТАКСИС: range(start, stop, step)
x = range(3, 20, 4)
for n in x:
print(n)
#Gives output
>>> 3
>>> 7
>>> 11
>>> 15
>>> 19
Просто перепишите created_range
, чтобы добавить значение шага time_slice_in_hours
created__range=[start_time, end_time, time_slice_in_hours]
ВАРИАНТ 1
def _get_prices_per_time_slice(self, last_hours= 24,time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time, time_slice_in_hours],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
Однако синтаксис в официальной документации django query set api не упоминает параметр step в функции __range()
, то есть он может не поддерживаться created__range
.
ВАРИАНТ 2
В этом случае вы можете использовать приведенную ниже функцию, где вы можете вычислить временной диапазон x_time_slice_list
с помощью функции DateTimeRange
Python ( официальная документация) и оценить created__in
from datetimerange import DateTimeRange
from dateutil.relativedelta import relativedelta
def _get_prices_per_time_slice(self, last_hours= 24,time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
x_time_slice_list= []
time_range = DateTimeRange(start_time, end_time)
for value in time_range.range(relativedelta(hours=+time_slice_in_hours)):
x_time_slice_list.append(value)
qset = Price.objects.filter(
created__in= x_time_slice_list,
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
Вы можете добиться этого, выполнив фильтрацию временных срезов в python.
Я имею в виду, что если взять функцию, которая у вас есть, то есть
def _get_prices_per_time_slice(self, last_hours=24):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price')
return [r['price'] for r in qset]
И перепишите его следующим образом, чтобы возвращать созданные данные и обрабатывать фильтрацию в возвратном списке comprehension.
def _get_prices_per_time_slice(self, last_hours=24, time_slice_in_hours=4):
now = timezone.now()
end_time = now.replace(second = 0, microsecond = 0)
start_time = end_time - timedelta(hours=last_hours)
qset = Price.objects.filter(
created__range=[start_time, end_time],
created__minute=end_time.minute
).values('price', 'created')
return [r.get('price') for r in qset if r.get('created').hour % time_slice_in_hours == 0]