侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

DRF 时间戳的序列化和反序列化

barwe
2023-05-23 / 0 评论 / 0 点赞 / 1,148 阅读 / 2,240 字
温馨提示:
本文最后更新于 2023-05-23,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

我们一般在定义模型时会给每个模型加上两个日期字段记录数据的创建时间和更新时间:

class User(models.Model):
    # ...
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)

此时数据库中默认存储的是一个形如 "2023-05-17 17:24:28" 的日期时间字符串。

为了避免时间格式的混乱,我们在返回时间字段时统计将其处理成以毫秒为单位的时间戳。

序列化

在 list 或者 retrieve 时 DRF 会调用对应 Serializer 对数据对象进行序列化:将一个 python 语言的对象转换成一个可以 json 化的字典。

我们可以用 只读字段 的方式将内置的 datetime 对象转换为毫秒时间戳:

def to_nullable_timestamp(dt):
    """将 ISO 8601 格式的字符串转换为毫秒时间戳"""
    if dt is None:
        return None
    # js Date 对象需要的是毫秒数
    return int(dt.timestamp() * 1000)

class ModelSerializer(serializers.ModelSerializer):
    """
    - `created_at` & `updated_at` 这两个字段将被序列化成整数
    """

    created_at = serializers.SerializerMethodField()
    updated_at = serializers.SerializerMethodField()

    def get_created_at(self, obj):
        return to_nullable_timestamp(obj.created_at)

    def get_updated_at(self, obj):
        return to_nullable_timestamp(obj.updated_at)

其中 get_ 方法定义了 如何从对象中提取出可序列化的值

注意,Python datetime 模块使用的时间戳单位是秒,而 js 使用的时间戳单位是毫秒。

反序列化

对饮的,在 create, update 或者 updatePartial 时 DRF 需要对前端传过来的 JSON 数据 反序列化,即将 JSON 数据转换为 Python 内置的对象。

to_internal_value() 方法用于反序列化时对传入的数据进行验证和转换,其返回的结果应当可以正确的被转换成构建对象需要的字段值。

class SampleCreateSerializer(serializers.ModelSerializer):
    closed_at = serializers.DateTimeField(required=False)
    completed_at = serializers.DateTimeField(required=False)
    sampled_at = serializers.DateTimeField()
    sent_at = serializers.DateTimeField()

    class Meta:
        model = Sample
        fields = "__all__"

    def to_internal_value(self, data):
        for field_name in ("closed_at", "completed_at", "sampled_at", "sent_at"):
            if isinstance(data.get(field_name), int):
                data[field_name] = datetime.fromtimestamp(data[field_name] / 1000)
        return super().to_internal_value(data)

上面的例子中,我们在 to_internal_value() 方法内部将传入的毫秒时间戳一一转换成 datetime 对象。

实践

对于created_at & updated_at 这两个字段可以将其封装在基类中,这样对于每一个模型都可以自动序列化成时间戳:

class ModelSerializer(serializers.ModelSerializer):
    """
    - `created_at` & `updated_at` 这两个字段将被序列化成整数
    """

    created_at = serializers.SerializerMethodField()
    updated_at = serializers.SerializerMethodField()

    def get_created_at(self, obj):
        return to_nullable_timestamp(obj.created_at)

    def get_updated_at(self, obj):
        return to_nullable_timestamp(obj.updated_at)

此 ModelSerialzier 相当于扩展了 DRF 默认 ModelSerialzier 的功能,其他序列化器从此 ModelSerialzier 继承即可。

0

评论区