【问题标题】:DjangoRestFramework ModelSerializer DateTimeField only converting to current timezone at object creationDjangoRestFramework ModelSerializer DateTimeField 仅在对象创建时转换为当前时区
【发布时间】:2016-03-20 10:31:47
【问题描述】:

更新:此行为被识别为错误并在 django-rest-framework 3.7 中解决。序列化程序现在将根据服务器的时区一致地显示时间:https://github.com/tomchristie/django-rest-framework/issues/3732#issuecomment-267635612

我有一个 Django 项目,我希望用户处于某个时区。我的设置中有TIME_ZONE = 'Asia/Kolkata' 和USE_TZ = True。

我有一个包含日期时间字段的模型。当我第一次创建对象时,modelserializer 会给出带有尾随+5:30 的日期时间。令人讨厌的是,带有auto_now_add=True 的日期时间会给出带有尾随Z 的UTC 日期时间。我通过使该字段的默认值在当前时间可调用来解决此问题。

如果我在任何时候再次序列化对象,所有日期时间都采用 UTC 格式,结尾带有 Z。从 Django 文档中,我希望序列化程序使用当前时区,默认为 TIME_ZONE = 'Asia/Kolkata' 设置的默认时区。我已经用get_current_timezone_name() 检查了我认为的当前时区,它是'Asia/Kolkata'。在我看来,我什至尝试过使用activate('Asia/Kolkata'),但仍以 UTC 形式返回时间。

请注意,所有时间都是正确的(UTC 时间早于 5:30 小时),只是我希望转换时间。所有日期时间都按预期以 UTC 时间存储在数据库中。

【问题讨论】:

    标签: django datetime serialization django-rest-framework


    【解决方案1】:

    在这里查看文档:http://www.django-rest-framework.org/api-guide/fields/#datetimefield

    签名:DateTimeField(format=None, input_formats=None)

    format - 表示输出格式的字符串。如果未指定,则默认为与 DATETIME_FORMAT 设置键相同的值,除非设置,否则将为“iso-8601”。设置为格式字符串表示 to_representation 返回值应强制转换为字符串输出。格式字符串如下所述。将此值设置为 None 表示 Python 日期时间对象应由 to_representation 返回。在这种情况下,日期时间编码将由渲染器确定。

    当格​​式使用 None 值时,to_representation 将返回对象,最终输出表示将由渲染器类确定。

    对于 JSON,这意味着默认日期时间表示使用 ECMA 262 日期时间字符串规范。这是使用毫秒精度的 ISO 8601 的子集,包括 UTC 时区的“Z”后缀,例如:2013-01-29T12:34:56.123Z。

    因此,获取日期时间对象的 UTC (Z) 表示实际上是默认行为。

    当您通过 Djangorest 创建 或 更新 模型实例时,将使用 data kwarg 调用序列化程序,如果您使用列表则不会发生这种情况或详细视图。

    在这两种情况下,您的视图都将返回 serializer.data。在 create/update 操作的情况下,这将是 serializer.validated_data 的表示,而在 list/detail 操作的情况下,它将直接表示实例。

    在这两种情况下,表示是通过使用默认 kwarg format=None 调用 field.to_representation 来实现的,这将使该字段返回纯字符串值。

    奇迹发生了here:

    • create/update:验证返回一个时区感知对象,其中包括您的标准时区。通过调用其isoformat() 方法将其转换为字符串,并按原样返回。
    • list/retrieve:django ORM 将时间戳存储为 UTC。通过调用其isoformat() 方法将其转换为字符串,但DRF 将+00:00 替换为Z(参见上面的链接)。

    因此,要获得带有时区偏移的所需输出,您可以将 format=None 或自定义 strftime 字符串传递给序列化程序中的 DateTimeField。但是,您将始终使用+00:00 获得 UTC 时间,因为(幸运的是)数据就是这样存储的。

    如果您想要'Asia/Kolkata' 的实际偏移量,您可能必须定义自己的DateTimeField:

    from django.utils import timezone
    class CustomDateTimeField(serializers.DateTimeField):
        def to_representation(self, value):
            tz = timezone.get_default_timezone()
            # timezone.localtime() defaults to the current tz, you only
            # need the `tz` arg if the current tz != default tz
            value = timezone.localtime(value, timezone=tz)
            # py3 notation below, for py2 do:
            # return super(CustomDateTimeField, self).to_representation(value)
            return super().to_representation(value) 
    

    【讨论】:

    • 这并不能解释为什么我在创建对象后立即得到不同的结果。此外,从“当前时区是用于渲染的时区”中,我假设这要么覆盖 JSON 的“默认日期时间表示”,要么在从数据库检索后本地化日期时间。最后,Django 文档还明确指定:“在序列化感知日期时间时,包含 UTC 偏移量,如下所示:“2011-09-01T13:20:30+03:00”。
    • 我想我明白了,有趣的问题。
    • 嗯。从 Django 文档来看,如果这是预期的行为,我会感到惊讶,尽管看起来这是 Django 本身的问题,而不是 DjangoRestFramework。我会调查你的解决方法。
    • 我认为这是非常理想的行为,请参阅docs.djangoproject.com/en/1.9/topics/i18n/timezones/… 的第一句对象在内部存储为 UTC,并且仅在模板和表单中呈现为用户的时区(“当前时区”)。
    • 并不是说它们没有被转换成序列化器。你报价中的“唯一”对我来说没有出现。行为会像这样改变似乎也不必要地令人困惑。当然至少应该记录在案。我更喜欢始终使用 UTC 或始终使用当前时区的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-12
    相关资源
    最近更新 更多