我知道要修复make_aware,我可以拦截例外并将其设置一个小时,但是问题是django在QuerySet中发现了django在日期上运行make_aware,我不是在代码中调用该函数,而是在Django库代码中进行的,而我不能编辑它,而不必在代码库的每个版本中包括修改版本。是否有工作?
请注意:再次,为了清楚起见,我没有手动致电make_aware,在评估
我遇到了相同的问题,并且该解决方案似乎在任何地方都没有记录下来,但是我得到了一个用几行代码工作的。答案有一个自定义的Django DB后端。我们要做的是扩展现有的MySQL DB后端,并替换使MySQL DateTime TimeZone Aware的代码片段。
首先,让我描述我面临的问题。2021年11月6日,PST时区进行了DST过渡,时钟回到了凌晨1:59之后,而不是增加到凌晨2点,就像在正常情况下会发生。结果,2021年11月6日的凌晨1点时间戳对Django来说都是模棱两可的,因为不确定是在回滚之前还是在回滚之前的凌晨1点(即,在正常情况下应该是凌晨2点,")。这个问题发生在每分钟和凌晨1点,从1:00:00到1:59:59。
i挖掘出错误,并注意到问题是在以下代码中,当Django Orm从数据库转换DateTimeField值时,这是由Django Orm调用的:
# in file django.db.backends.mysql.operations
class DatabaseOperations(BaseDatabaseOperations):
# other code here
def convert_datetimefield_value(self, value, expression, connection):
if value is not None:
value = timezone.make_aware(value, self.connection.timezone)
return value
特别是,问题在以下行中:
value = timezone.make_aware(value, self.connection.timezone)
由于它没有将is_dst
参数传递给timezone.make_aware
调用,因此我们必须在一定条件下获得AmbiguousTimeError
。一个简单的解决方案是将is_dst=True
传递到呼叫(或False
,选择是任意的),但这意味着修改Django的代码...还是会修改?您无需修改Django的代码,您可以简单地扩展默认的MySQL DB后端并通过自己的实现来覆盖该功能!(我一开始已经宠坏了这一点,但是您仍然应该为额外的戏剧性效果感到惊讶)
Django 2、3和4
的解决方案- 扩展默认的Django mysql db后端,并覆盖执行DateTime字段转换的代码部分。为了做到这一点,请在Django项目的根部创建一个新软件包,并在其中包含一个名为
base.py
的文件。在该文件中,我们的自定义DJANGO DB后端代码将使用。我打电话给数据库后端illyadbengine
,所以我将具有以下文件夹结构:
my-django-app
my-django-app
- settings.py
illyadbengine
- __init__.py
- base.py
并将以下代码放在base.py
:
"""
A custom MySQL DB engine that solves the ambiguous time error during DST transition, by assuming that it is DST
in case of an error.
"""
from django.db.backends.mysql import base
from django.utils import timezone
from django.db.backends.mysql.operations import DatabaseOperations
from pytz.exceptions import AmbiguousTimeError
class MySQLOperationsWithDSTConflictResolutionOperations(DatabaseOperations):
def convert_datetimefield_value(self, value, expression, connection):
try:
# attempt at performing the default conversion, and only fallback to DST conflict resolution if it fails
return super().convert_datetimefield_value(value, expression, connection)
except AmbiguousTimeError:
if value is not None:
# NOTE: this can cause an error by 1 hour, since it always assumes DST timezone in case of conflict
# if a precise time conversion is important for your use case, you should write that custom logic here
value = timezone.make_aware(value, self.connection.timezone, is_dst=True)
return value
class DatabaseWrapper(base.DatabaseWrapper):
ops_class = MySQLOperationsWithDSTConflictResolutionOperations
- 指示您的数据库在
settings.py
中使用自定义引擎。您可以以各种方式进行操作,具体取决于您如何在设置中定义的数据库连接。您正在寻找的是将数据库连接的ENGINE
参数设置为illyadbengine
。
因此,如果您使用的DB使用连接字符串,则可以执行以下操作:
import environ
env = environ.Env()
DATABASES = {
"default": env.db_url("DATABASE_URL"),
}
DATABASES["default"]["ENGINE"] = "illyadbengine"
,或者如果您使用inline dict
定义连接元素,则会有类似的东西:
DATABASES = {
'default': {
# other settings here
'ENGINE': 'illyadbengine',
}
}
请注意,该解决方案假设DST时间在冲突期间有效,因此您可能会在夏季/冬季时间过渡的小时内的实时时间下一个小时。如果您的应用程序确切地应用至关重要,则应在实施Overriden convert_datetimefield_value
中包括该信息。
此解决方案将适用于Django 2,Django 3和Django 4。
我发现了一些文档,以在Django的3个文档中扩展数据库引擎,尽管Django 2缺席,但我在Django 2.2中自己对其进行了测试,并且可以正常进行。您应该在Django 4中没有问题,因为该文档也存在。