在 Django 中,如何构建我的查询集以在时间片上按时间进行过滤?

In Django, how do I construct my queryset to filter by time over slices of time?

我正在使用 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 小时筛选一次价格?

python中的range函数有助于指定增量步长值

语法: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 official documentation 中的语法未能在 __range() 函数中提及步骤参数,即 created__range 可能不支持它。

选项 2

在这种情况下,您可以使用下面的函数,您可以使用 Python 的 DateTimeRange 函数(official documentation)计算时间范围 x_time_slice_list 并评估为 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]

并按照您的方式将其重写为 return 创建的数据并处理 return 列表理解中的过滤。

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]