【问题标题】:How to use default datetime serialization in Django REST Framework?如何在 Django REST Framework 中使用默认的日期时间序列化?
【发布时间】:2019-02-14 21:42:37
【问题描述】:

我有一个 Django REST Framework 序列化程序,其中包含以下内容:

from rest_framework import serializers


class ThingSerializer(serializers.ModelSerializer):
    last_changed = serializers.SerializerMethodField(read_only=True)

    def get_last_changed(self, instance: Thing) -> str:
        log_entry = LogEntry.objects.get_for_object(instance).latest()

        representation: str = serializers.DateTimeField('%Y-%m-%dT%H:%M:%SZ').to_representation(log_entry.timestamp)
        return representation

这是有问题的,因为如果日期时间格式发生变化,它将与所有其他 datetimes 不同。我想重用 DRF 用来序列化其他 datetime 字段的代码路径。

到目前为止我已经尝试过:

  • 唯一看起来相关的 answer 实际上并没有产生与 DRF 相同的结果(它包括毫秒,而 DRF 没有),大概是因为它使用的是 Django 而不是 DRF 序列化程序。
  • rest_framework.serializers.DateTimeField().to_representation(log_entry.timestamp)rest_framework.fields.DateTimeField().to_representation(log_entry.timestamp)rest_framework.fields.DateTimeField(format=api_settings.DATETIME_FORMAT).to_representation(log_entry.timestamp) 也不起作用;他们产生具有微秒精度的字符串。我已经用调试器验证了 DRF 在序列化其他字段时调用后者,所以我不明白为什么它会在我的情况下产生不同的结果。
  • LogEntry.timestamp 被声明为django.db.DateTimeField,但如果我尝试LogEntry.timestamp.to_representation(log_entry.timestamp) 之类的东西,它会严重失败:

    AttributeError: 'DeferredAttribute' 对象没有属性 'to_representation'

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    翻看DRF的源码,有趣的事情发生在rest_framework/fields.py

    特别是,所有格式化内容都直接在 DateTimeField.to_representation 方法中进行。

    您有几种方法可以复制 DRF 的行为。


    首先,您根本无法传递格式。如果您没有明确提供格式,则 DRF 应使用其默认值。

    representation: str = serializers.DateTimeField().to_representation(log_entry.timestamp)
    

    或者,继续做你正在做的事情,但明确地传递来自 DRF 的api_settings.DATETIME_FORMAT 的格式字符串。这可能感觉不那么神奇,但老实说,未来 API 更改可能会更加脆弱。

    这可能看起来像:

    from rest_framework.settings import api_settings
    ...
    representation: str = serializers.DateTimeField(api_settings.DATETIME_FORMAT).to_representation(log_entry.timestamp)
    

    但是,鉴于您尝试了第一次但失败了,我们需要深入了解一下!

    DRF 的默认 DateFormat 是 ISO_8601,其中包含 following code

    value = value.isoformat()
    if value.endswith('+00:00'):
        value = value[:-6] + 'Z'
        return value
    

    也就是说,它实际上只是依赖于 python isoformat 函数。

    isoformat 的格式会有所不同如果值有微秒或没有

    来自Python docsisoformat 将:

    以 ISO 8601 格式返回一个表示日期和时间的字符串,YYYY-MM-DDTHH:MM:SS.ffffff,如果微秒为 0,则返回 YYYY-MM-DDTHH:MM:SS

    在这种情况下,解决方案是在时间戳中将微秒显式设置为。有几种方法可以做到这一点,但我们可以切换到 Unix 时间戳,剪辑到秒,然后再返回

    ts = int(log_entry.timestamp)
    representation: str = serializers.DateTimeField().to_representation(ts)
    

    或继续直接使用 DateTime 对象,这将有更好的时区处理:

    representation: str = serializers.DateTimeField().to_representation(
            logentry.replace(microsecond=0)
        )
    

    【讨论】:

    • 我已经尝试过第一个,它产生的格式与我所有其他日期时间字段的格式不同……
    • 你是对的,毫秒改变格式!弗里格!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 2015-09-02
    • 1970-01-01
    • 2014-08-12
    • 2021-04-19
    • 1970-01-01
    相关资源
    最近更新 更多