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

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

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

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

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

最佳答案

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


签名:DateTimeField(格式=无,input_formats =无)

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

如果将None值用于格式,则datetime对象将由to_representation返回,而最终的输出表示形式将由renderer类确定。

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


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

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

在这两种情况下,您的视图都将返回serializer.data。在创建/更新操作的情况下,这将是serializer.validated_data的表示,而在列表/详细操作的情况下,它将是实例的直接表示。

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

魔术发生在here上:


创建/更新:验证将返回一个时区感知对象,其中包括您的标准时区。通过调用其isoformat()方法将其转换为字符串,并按原样返回。
列表/检索: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)

07-25 22:36
查看更多