You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Pydantic中修改日期格式?如何兼顾验证与序列化场景的日期格式配置(当前验证使用@validator)?

在Pydantic中修改日期格式及适配验证与序列化的方案

嘿,这个问题问得很实用!很多人用Pydantic处理日期时都会遇到既要验证输入格式,又要控制序列化输出的需求,我来帮你梳理几种靠谱的方案,尤其是能同时搞定这两个场景的优雅写法。

一、单独处理验证或序列化的基础方式

如果你暂时只需要搞定其中一个场景,可以先试试这两种简单方法:

1. 仅验证环节(适配v1/v2)

你现在用的@validator是Pydantic v1的写法,v2里已经改成了@field_validator,核心逻辑是在数据进入模型前,把指定格式的字符串转成datetime对象:

Pydantic v2 示例

from pydantic import BaseModel, field_validator
from datetime import datetime

class Event(BaseModel):
    event_date: datetime

    @field_validator('event_date', mode='before')
    def parse_event_date(cls, value):
        # 这里指定输入要符合的格式,比如YYYY-MM-DD HH:mm:ss
        if isinstance(value, str):
            return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
        return value

Pydantic v1 示例

from pydantic import BaseModel, validator
from datetime import datetime

class Event(BaseModel):
    event_date: datetime

    @validator('event_date')
    def parse_event_date(cls, value):
        if isinstance(value, str):
            return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
        return value

2. 仅序列化环节(适配v1/v2)

如果只是想在把模型转成JSON时输出特定格式的日期字符串,v2用@field_serializer,v1用Config.json_encoders

Pydantic v2 示例

from pydantic import BaseModel, field_serializer
from datetime import datetime

class Event(BaseModel):
    event_date: datetime

    @field_serializer('event_date')
    def serialize_event_date(self, value: datetime):
        # 指定输出格式,比如YYYY/MM/DD
        return value.strftime('%Y/%m/%d')

Pydantic v1 示例

from pydantic import BaseModel
from datetime import datetime

class Event(BaseModel):
    event_date: datetime

    class Config:
        json_encoders = {
            datetime: lambda v: v.strftime('%Y/%m/%d')
        }

二、同时适配验证与序列化的优雅方案

上面分开处理的方法有点繁琐,下面两种方案能一次性搞定验证和序列化,复用性还强:

1. 使用Annotated绑定逻辑(v2推荐)

结合AnnotatedBeforeValidatorPlainSerializer,可以把验证和序列化逻辑封装成一个可复用的类型,直接在字段里使用:

from pydantic import BaseModel, BeforeValidator, PlainSerializer, WithJsonSchema
from datetime import datetime
from typing import Annotated

# 定义自定义日期类型,绑定验证和序列化规则
CustomDate = Annotated[
    datetime,
    # 验证:把指定格式的字符串转成datetime
    BeforeValidator(lambda v: datetime.strptime(v, '%Y-%m-%d') if isinstance(v, str) else v),
    # 序列化:把datetime转成指定格式的字符串
    PlainSerializer(lambda v: v.strftime('%Y/%m/%d') if isinstance(v, datetime) else v),
    # 可选:给OpenAPI文档加格式说明
    WithJsonSchema({'type': 'string', 'format': 'date'}),
]

class Event(BaseModel):
    event_date: CustomDate

这样不管输入是"2024-05-20"字符串还是直接传datetime对象,都会被正确验证;序列化时会输出"2024/05/20"格式的字符串,完美兼顾两边。

2. 自定义Pydantic类型(灵活适配v1/v2)

如果需要更复杂的逻辑(比如多种格式兼容),或者要在多个模型里复用,可以自定义一个继承自datetime的Pydantic类型:

Pydantic v2 示例

from pydantic import GetJsonSchemaHandler
from pydantic_core import core_schema
from datetime import datetime
from pydantic import BaseModel

class CustomDateTime(datetime):
    @classmethod
    def __get_pydantic_core_schema__(cls, source_type, handler: GetJsonSchemaHandler):
        # 定义核心验证和序列化规则
        return core_schema.no_info_wrap_validator_function(
            cls.validate,
            core_schema.union_schema([
                core_schema.is_instance(datetime),
                core_schema.str_schema(),
            ]),
            serialization=core_schema.plain_serializer_function_ser_schema(
                cls.serialize,
                return_schema=core_schema.str_schema(),
            ),
        )

    @classmethod
    def validate(cls, value):
        if isinstance(value, datetime):
            return value
        try:
            # 支持的输入格式
            return datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
        except ValueError:
            raise ValueError(f"无效的日期格式,预期格式为'YYYY-MM-DD HH:mm:ss',实际收到{value}")

    @classmethod
    def serialize(cls, value):
        # 序列化输出的格式
        return value.strftime('%Y/%m/%d %H:%M:%S')

class Event(BaseModel):
    event_date: CustomDateTime

这个自定义类型会自动处理输入验证和序列化输出,而且可以在任何模型里直接使用,非常灵活。

Pydantic v1 兼容写法
如果还在使用v1,可以通过@validatorjson_encoders结合的方式实现类似效果,就是开头提到的分开处理的方法,虽然不如v2优雅,但也能满足需求。

内容的提问来源于stack exchange,提问作者jonsbox

火山引擎 最新活动