【问题标题】:How do I filter by time in a date time field?如何在日期时间字段中按时间过滤?
【发布时间】:2009-05-27 21:03:17
【问题描述】:

在模型中“输入”是一个日期时间字段。我想查询数据以查找中午(开始时间)和下午 5:00(结束时间)之间的所有条目。

selected = Entry.objects.filter(entered__gte=start_time, entered__lte=end_time)

(如我所料)我收到以下错误:

"ValidationError: Enter a valid date/time in YYYY-MM-DD HH:MM[:ss[.uuuuuu]] format."

所以我知道我可以使用 __year 所以我尝试了。

selected = Entry.objects.filter(entered__time__gte=start_time, entered__time__lte=end_time)

我收到以下错误:

"FieldError: Join on field 'start' not permitted. Did you misspell 'time' for the lookup type?"

【问题讨论】:

    标签: python django


    【解决方案1】:

    我不相信有对此的内置支持,但您可以传递 extra where-clause 参数(警告:这里可能会引入依赖于 DB 的行为)。

    例如,在 Postgres 上,类似:

    Entry.objects.extra(where=['EXTRACT(hour from entered) >= 12 and '\
                        'EXTRACT(hour from entered) < 17'])
    

    如果您使用可能不安全的输入来确定值 1217,请注意,您还可以为 extra 指定一个 params 选项,以确保正确引用和转义,然后使用标准 sql @987654325在 where 语句中使用 @ 占位符。

    【讨论】:

    • 我正要发布一些类似的内容。使用 SQLite,Entry.objects.extra(where=["time(entered) between '12:00' and '17:00'"]) 也应该这样做。
    • 当然不用自定义sql也可以要求时间范围。看我的回答
    【解决方案2】:

    您可以使用范围在两者之间进行过滤。我发现这是最好的过滤方式,因为 SQL 的过滤效果比 Django 好。

    fromRange=datetime.datetime.strptime(request.GET.get('from'),'%Y-%m-%dT%H:%M:%S.%fZ')
    toRange=datetime.datetime.strptime(request.GET.get('to'),'%Y-%m-%dT%H:%M:%S.%fZ')
    
    entry = Entry.objects.filter(entryTime__range=(fromRange,toRange))
    

    【讨论】:

      【解决方案3】:

      您可以使用 db 的机制在 python 中进行过滤:

      for e in Entry.objects.all():
         if i.entered.hour>= 9 and i.entered.hour < 17 :# or break down to minutes/seconds
              list.append(e)
      

      但我认为这两种解决方案都很丑。

      史蒂夫,你必须决定,什么对你来说不那么丑:

      • 在 for 循环中处理大量数据,
      • 或使用 .extra(..) 并绕过 orm 系统

      【讨论】:

      • 到目前为止,这是我更喜欢的选项。我仍然想要一个更好的选择。
      • 可以在 django 1.8 中按小时/分钟过滤:docs.djangoproject.com/en/1.8/ref/models/querysets/#hour Entry.objects.filter(entered__hour__gte=9, entered__hour__lte=17)
      • 不幸的是,这在 Django 1.8 中是不可能的。您只能过滤精确的小时匹配:Entry.objects.filter(entered__hour=9)
      • 您是否尝试过过滤 BIG DAT?在 python 中的过滤比在带有索引等的数据库中要慢得多。
      【解决方案4】:

      以 SQLite 为例,一个相对干净和通用的解决方案是:

      Entry.objects.extra(where=["time(entered) between '%s' and '%s'"],
                          params=[start_time.strftime("%H:%M"), end_time.strftime("%H:%M")])
      

      【讨论】:

      • 这个解决方案有一个小bug,让我挣扎了半个小时:你不需要把%s放在引号里,所以应该是"time(entered) between %s and %s"。至少现在是 2015 年的情况。此外,extra() 现在已弃用,他们建议改用RawSql
      【解决方案5】:

      您是否为start_timeend_time 提供了datetime objects

      快速试用:

      class Entry(models.Model):
          entered = models.DateTimeField()
      
      >>> from datetime import datetime
      >>> Entry(entered = datetime.now()).save()
      >>> Entry.objects.filter(entered__lte = datetime.now())
      [<Entry: Entry object>]
      >>> Entry.objects.filter(entered__gte = datetime.now())
      []
      >>> Entry.objects.filter(entered__gte = datetime.now(), entered__lte=datetime(2009,11,1,0,0))
      [<Entry: Entry object>]
      

      【讨论】:

      • 是的,但您并没有真正回答这个问题:它应该返回下午 12 点到 7 点之间的每个条目,无论是什么日期。
      • 我不想只按小时过滤时间可能是 12:34:54 到 14:23:06,重要的是过滤器只考虑时间而不考虑日期.
      • 恐怕使用extra()修饰符是唯一的选择。 Steven,你使用的是什么数据库后端?
      【解决方案6】:

      我建议过滤 x&lt;=arg 并排除 x&gt;=arg2

      【讨论】:

        【解决方案7】:

        我刚刚为类似的用例找到了一个解决方案——也许有人觉得这很有用:

        就我而言,我有这样的可用性和约束模型(简化):

        class Availability(...):
            start = models.DateTimeField()
            end = models.DateTimeField()
        
        class Constraint(...):
            from = models.TimeField()
            to = models.TimeField()
        

        我想找到不违反任何限制的可用性。

        使用 Postgres,这对我有用:

        Availability.objects.extra(
            where=['NOT (start::time, "end"::time) OVERLAPS (\'{0}\'::time, \'{1}\'::time)'.format(
                from.strftime("%H:%M"), to.strftime("%H:%M")
            )]
        )
        

        请注意如何将DateTime 转换为Time(或者更确切地说,在Postgres 术语中将timestamp 转换为time without timezone)与::time,然后使用OVERLAPS 来比较时间范围。

        关于原来的问题,你也可以这样做:

        Availability.objects.extra(
            where=['start::time BETWEEN \'{0}\'::time AND \'{1}\'::time AND
                    "end"::time BETWEEN \'{0}\'::time AND \'{1}\'::time'.format(
                from.strftime("%H:%M"), to.strftime("%H:%M")
            )]
        )
        

        【讨论】:

          猜你喜欢
          • 2015-03-06
          • 1970-01-01
          • 2017-08-04
          • 1970-01-01
          • 2020-11-27
          • 2018-02-21
          • 2013-10-20
          • 2021-07-03
          • 2016-12-09
          相关资源
          最近更新 更多