在 DRF 中使用与 models.DateTimeField 相同的日期/时间格式

Posted

技术标签:

【中文标题】在 DRF 中使用与 models.DateTimeField 相同的日期/时间格式【英文标题】:Use same date/time format in DRF as for models.DateTimeField 【发布时间】:2015-08-15 23:44:11 【问题描述】:

我有一个带有start_time 字段的Build 模型,该字段的类型为models.DateTimeField。另一方面,我有一个包含 start_time 字段的 BuildSerializer 类。现在,当我在模板中打印时间戳时,会得到如下结果:

 build.start_time|format:'DATETIME_FORMAT' 

在我的模板中会变成:

April 24, 2015, 8:03 a.m.

但是,序列化器输出不同的值:

2015-04-24T08:03:39.336922Z

这很好,因为它是标准的 JSON 日期表示。但是,我想根据用户的语言环境来显示它,就像 Django 在我使用上述模板 sn-p 时所做的那样。

我尝试使用

timestamp = serializers.DateTimeField(format=settings.DATETIME_FORMAT)

但 DRF 使用不同的方式来格式化日期输出,所以这只会输出:

"N j, Y, P"

对于默认的en-us 语言环境。

有没有办法像 Django 一样格式化日期?我对 python 或 javascript 解决方案都感兴趣,因为无论如何我都会在 JS 函数(AJAX 回调)中处理 JSON。

【问题讨论】:

【参考方案1】:

我会强烈建议将您的日期格式保持为 ISO 8601(这是默认值),因为它是一致且标准化的日期格式,因此您无需过多担心在任何地方手动解析日期。大多数语言都可以解析 ISO 8601 日期,而那些默认情况下不能解析的语言通常都有可以为您解析的库。

有没有办法像 Django 一样格式化日期?

默认情况下,Django REST 框架仅支持使用单个格式化程序格式化与日期相关的字段,默认情况下为 ISO 8601。这是为了总体一致性,因此客户端可以发送OPTIONS 请求并直接从那里解析日期格式并依赖于永不改变的日期格式。

可以通过在模型上使用属性或覆盖返回值的SerializerMethodField 来覆盖此行为,以便在您想本地化后端。或者,您可以覆盖默认的DateTimeFieldto_native 方法并在那里进行格式化,这样您就可以拥有一个可以跨序列化程序使用的字段,从而使本地化保持一致。

我对 python 或 JavaScript 解决方案都感兴趣,因为无论如何我都会在 JS 函数(AJAX 回调)中处理 JSON。

我们用于解析 ISO 8601 次的 JavaScript 解决方案是 Moment.js。虽然某些浏览器可以解析 ISO 8601 次,但并非所有浏览器都可以,而且将它们传递给 Moment 更容易,以便它们可以在 JavaScript Date objects 中转换。

// parse the incoming date/time string
var start = moment(response.start_time, moment.ISO_8601);

moment 的第一个参数是要解析的字符串,第二个参数是要解析的格式。 moment.ISO_8601 是一个特殊的钩子,可以自动将其解析为有效的 ISO 8601 时间,这就是我们使用它的原因。

从那里您可以使用浏览器的日期本地化向用户显示时间的本地化版本。 Date 对象提供toLocaleString()toLocaleDateString()toLocaleTimeString() 方法,可用于将日期的不同部分(或整个事物)格式化为用户期望的格式。

在 Chrome 上本地测试(在 Ubuntu,en-US)我得到以下方法的输出

> (new Date()).toLocaleString()
< "6/2/2015, 7:23:03 PM"
> (new Date()).toLocaleDateString()
< "6/2/2015"
> (new Date()).toLocaleTimeString()
< "7:23:12 PM"

这就是我为此浏览器配置日期设置的方式。这具有始终使用系统当前时区的额外好处,因此您无需担心本地化的那部分 - 它会自动处理。

【讨论】:

很好的答案,有很多细节,我很感激!问题的根源在于我们在两个不同的地方生成了相同的列表:在 html 模板中和从 DRF 序列化程序生成的 JSON 响应中。我知道这违反了 DRY 原则,我们目前正试图消除它,但使用当前使用的方法很难不降低网站的可用性。这就是为什么我找到了您可以在我自己的答案中阅读的解决方案。 我来自谷歌,您可以通过 2021 更新扩展答案,我们可以在本地 Date 对象上使用 toISOString。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…【参考方案2】:

我很快就找到了解决方案,虽然我认为它有点难看:

from django.utils import formats

class BuildSerializer(serializers.ModelSerializer):
    timestamp_django = serializers.SerializerMethodField()

    def get_timestamp_django(self, obj):
       return formats.date_format(obj.timestamp, 'DATETIME_FORMAT')

    class Meta:
        model = Build
        fields = ('timestamp', 'timestamp_django', …)

这样我可以两全其美,因为我仍然可以将 JSON 日期用于其他目的(如过滤或排序)。

【讨论】:

【参考方案3】:

这是我的解决方案,基于Kevin Brown's answer:

from django.utils import formats
from rest_framework import serializers
from rest_framework.settings import api_settings

class DateTimeField(serializers.DateTimeField):
    """DateTime field whichs supports Django's format definitions."""

    def to_representation(self, obj):
        if api_settings.DATETIME_FORMAT in formats.FORMAT_SETTINGS:
            return formats.date_format(obj, api_settings.DATETIME_FORMAT)
        return super().to_representation(obj)


class DateField(serializers.DateField):
    """Date field whichs supports Django's format definitions."""

    def to_representation(self, obj):
        if api_settings.DATE_FORMAT in formats.FORMAT_SETTINGS:
            return formats.date_format(obj, api_settings.DATE_FORMAT)
        return super().to_representation(obj)


class TimeField(serializers.TimeField):
    """Time field whichs supports Django's format definitions."""

    def to_representation(self, obj):
        if api_settings.TIME_FORMAT in formats.FORMAT_SETTINGS:
            return formats.date_format(obj, api_settings.TIME_FORMAT)
        return super().to_representation(obj)

【讨论】:

以上是关于在 DRF 中使用与 models.DateTimeField 相同的日期/时间格式的主要内容,如果未能解决你的问题,请参考以下文章

drf Serializer使用

在 DRF 中使用与 models.DateTimeField 相同的日期/时间格式

DRF视图-请求与响应

Django 表单验证与 DRF 序列化程序验证

drf 过滤器组件与自定义过滤器

Django(42)DRF安装与使用