过滤Django日期时间字段按月份和日期出现问题

26
有人能解释一下为什么以下的过滤器在月份和日期级别上不起作用吗?仅按年份过滤似乎可行,但其他两个不行。
>>> clicks.count()
36
>>> date = clicks[0].created
>>> date.month
2
>>> date.year
2014
>>> date.day
1
>>> clicks.filter(created__month=2)
[]
>>> clicks.filter(created__month=02)
[]
>>> clicks.filter(created__month='02')
[]
>>> clicks.filter(created__month='2')
[]
>>> clicks.filter(created__month=date.month)
[]
>>> clicks.filter(created__day=date.day)
[]

迅速更新以展示在创建和处理查询集之前我遇到了相同的问题:

>>> clicks = PreviewClick.objects.filter(created__month = 2)
>>> clicks.count()
0
>>> clicks = PreviewClick.objects.filter(created__month = 02)
>>> clicks.count()
0
>>> clicks = PreviewClick.objects.filter(created__month = '02')
>>> clicks.count()
0
>>> clicks = PreviewClick.objects.filter(created__month = '2')
>>> clicks.count()
0

以下是更多值得思考的内容:

>>> clicks = PreviewClick.objects.all()
>>> counter = 0
>>> for click in clicks:
...      if click.created.month == 2:
...           counter += 1
... 
>>> counter
35

1
看起来你执行了 <Model>.objects.all() 并将其分配给 clicks,然后尝试对其进行过滤。你可能想执行 <Model>.objects.filter(....)。 - Hoopdady
@user590028 在第二行,我获取了查询集中第一个项目的实际“created”值,并证明月份值实际上是二月。因此,过滤器应该至少获取一条记录,对吗? - zorrotmm
@Hoopdady 哈哈,没错,我也在想同样的事情。在你提问的时候,我已经更新了那个信息。 - zorrotmm
你的模型里没有什么花哨的东西,对吧? - Hoopdady
我和你@zorrotmm有完全相同的问题。 Django版本1.6.1 Python版本2.7.5 Mac OS系统我正在使用的模型字段是models.DateTimeField。.filter(datetime__year=2014)可以工作, 但是.filter(datetime__month=2)不行, .filter(datetime__day=25)也不行。如果我找到了解决方案,我会更新的。 - SWilder
显示剩余4条评论
5个回答

26

我遇到了和你完全相同的问题。

如果您查看1.6版本的文档和月份查询集,请查看文档。他们添加了以下段落:

“当 USE_TZ 设置为 True 时,在过滤之前,日期时间字段会转换为当前时区。这需要数据库中的时区定义。”

如果您将设置中的以下行更改为 False,则应开始收到您希望得到的数据。

USE_TZ = False

嗯...有趣。我会试一试并看看会发生什么。我猜我对处理时区还很陌生——我正在尝试根据时区正确过滤数据,虽然实际的数据库记录只使用UTC。我一直在视图中进行时区计算。如果我是这样处理它,我是否不需要使用USE_TZ呢? - zorrotmm
很抱歉,我没有处理时区的经验,并且在我的当前项目中也不需要处理时区,所以我对此的修复已经足够了。但我希望这些信息能够为您提供所需的帮助,让您的代码再次正常工作。 - SWilder

24

@Simon Wilder 解答了为什么它不起作用,以下是你可以实际解决它而无需在 Django 中禁用 TZ 支持的方法

Django 文档提供了安装数据库时区定义的说明

SQLite:安装 pytz - 转换实际上在 Python 中执行。

PostgreSQL:没有要求(参见时区)。

Oracle:没有要求(参见选择时区文件)。

MySQL:安装 pytz 并使用 mysql_tzinfo_to_sql 加载时区表。

在我的情况下:使用 mysql 和 Mac OS,以下命令解决了这个问题:

sudo mysql_tzinfo_to_sql /usr/share/zoneinfo/ | mysql -u root mysql

3
确实,这应该是正确的答案。几乎每个项目都应该使用时区感知日期。 - rollingthedice

2

您的语法不正确。应该是:

Clicks.objects.filter(created__month=2)

(你忘记了“对象”管理器)


我想我没有明确说明clicks是一个queryset,我已经用.all()或按年份2014进行过滤获取了它,这似乎有效。filter()函数应该也是一样的。无论如何,我会更新问题以展示在创建queryset之前我得到了相同的结果。 - zorrotmm

0

在这里更新答案,因为我遇到了上述问题,但是没有一个解决方案可行。大多数新的mysql安装都预装了tz-info,因此mysql_tzinfo_to_sql命令不会真正有所帮助。而将TZ_INFO设置为False并不是一个解决方案,因为许多人需要时区感知的日期时间。

因此,对我有用的是创建一个tz感知的datetime对象并针对该对象进行检查。假设您想要过滤今天的记录,则可以执行以下操作:

from datetime import datetime
import pytz

today = datetime.now().replace(tzinfo=pytz.UTC).date()   # tz aware datetime object
todays_records = myModel.objects.filter(created__year=today.year, created__month=today.month,created__day=today.day)

希望这能有所帮助。

0

Windows解决方案,2022年 - 如何使带有USE_TZ=True的Django能够按月和日进行过滤,而不仅仅是年份。现在工作代码示例:queryset.filter(created__month=1)

  • 从这里下载时区文件:https://dev.mysql.com/downloads/timezones.html
  • 确保您获取正确的文件 - 对于mysql8,我使用了5.7的文件
  • 解压缩它 - 确认它只是一个sql文件
  • 将其加载到您的mysql数据库中。类似于

mysql -u root -p mysql < FILEPATH

  • 重新启动mysql服务 (控制面板 | 管理工具 | 服务 | 找到mysql | 重启)
  • 验证: mysql | use mysql | select * from time_zone
  • 你应该会看到几千条记录。如果没有,这意味着你没有正确地加载sql文件。
  • 使用筛选器重新运行django方法,它应该能正常工作。

注:这与上面varnothing的答案不同,因为在Windows上,处理文件类型或像mysql_tzinfo_to_sql这样的东西可能会由于路径不同而有所不同。这个解决方案适用,因为你可以采取明确的操作,比如“将这个sql文件加载到这个数据库中”。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接