如何在Pydantic中修改日期格式?如何兼顾验证与序列化场景的日期格式配置(当前验证使用@validator)?
嘿,这个问题问得很实用!很多人用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推荐)
结合Annotated、BeforeValidator和PlainSerializer,可以把验证和序列化逻辑封装成一个可复用的类型,直接在字段里使用:
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,可以通过@validator和json_encoders结合的方式实现类似效果,就是开头提到的分开处理的方法,虽然不如v2优雅,但也能满足需求。
内容的提问来源于stack exchange,提问作者jonsbox




