Django REST Framework中把DatetimeField转换为时间戳

目的

普通DateTimeField会在Django REST Framework中输出的JSON中,转换成字符串的形式,如"2019-06-10T00:49:51.689440+08:00"。 并且也只接受对应格式字符串的输入。 这对DateField也许还比较方便,但对DateTimeField来说是不可接受的。

DateTimeField只需要在显示时,由客户端(浏览器或移动App)转换为字符串,而传输时则最好是一个float。 否则,客户端会需要比较复杂的日期与时区转换操作,传输的数据量也比较大。

本文介绍如何利用Django REST Framework中的自定义Feild,把存储层的datetime转换为float形式的timestamp

其它方案

通过格式化,可以直接做到DateTimeField转换为Timestamp形式的字符串,比如"1560098991.689440"。 但是却无法做到识别这种字符串的输入,造成输入输出的不均衡。

settings.py中添加:

REST_FRAMEWORK = {
    'DATETIME_FORMAT': '%s.%f',
    'DATETIME_INPUT_FORMATS': ['%s.%f', 'iso-8601'],
}

这里输出虽然是1560098991.689440,但是输入却必须是ISO 8601标准的"2019-06-10T00:49:51.689440+08:00"。 那个'%s.%f',输出时没问题,输入时却会被解析为ss.uuuuuu的形式,丢失了秒以上的信息。 也就是说,只能输入"91.689440"这种字符串,时间当然不对。

可行方案

利用Django REST Framework中的自定义Field,可以在存储层的datetime和表现层的timestamp之间做转换。

Model样例

from django.db.models import DateTimeField, Model

class Example(Model):
    datetime = DateTimeField()

Serializers

from datetime import datetime

from pytz import timezone
from rest_framework.serializers import Field, HyperlinkedModelSerializer

from ..settings import TIME_ZONE

class TimestampField(Field):
    """
    This serializer field transform a datetime str to a timestamp float.
    """

    def to_representation(self, value):
        return value.timestamp()

    def to_internal_value(self, data):
        timestamp = float(data)
        no_tz = datetime.utcfromtimestamp(timestamp)
        return no_tz.astimezone(timezone(TIME_ZONE))


class ExampleSerializer(HyperlinkedModelSerializer):
    timestamp = TimestampField(source='datetime')

    class Meta:
        model = Frame
        fields = (
            'url',
            'timestamp',
        )

如果不在datetime中放入时区信息,则会有以下警告:

RuntimeWarning: DateTimeField Frame.datetime received a naive datetime (2019-06-09 16:49:51.689440) while time zone support is active.

参考


相关笔记