В 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]

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