Pydantic 2.7.0模型:如何将空字符串转为None并验证日期时间
问题原因
你遇到的问题核心在于:Annotated[datetime, BeforeValidator(parse_datetime)] 中的验证器仅作用于 datetime 类型的校验流程。当输入空字符串时,验证器返回 None,但后续Pydantic仍会检查该值是否符合 datetime 类型,因此触发了类型不匹配的错误。而直接传入 None 时,字段的 date_time | None 类型会直接接受 None,不会进入 datetime 的校验流程,所以没有报错。
解决方案
这里提供两种可行的解决方式:
方式一:使用字段级验证器(field_validator)
直接给字段添加前置验证器,覆盖整个字段的校验逻辑,包括处理空字符串转 None、合法日期字符串转 datetime 以及直接接受 None 的场景:
from pydantic import BaseModel, ValidationError, field_validator from datetime import datetime from typing import Union class Model(BaseModel): dt: datetime | None @field_validator('dt', mode='before') def parse_datetime(cls, value): # 直接返回None if value is None: return None # 已经是datetime类型,直接返回 if isinstance(value, datetime): return value # 空字符串(含全空格)转为None if isinstance(value, str) and len(value.strip()) == 0: return None # 解析合法格式的日期字符串 try: return datetime.strptime(value, "%Y-%m-%d %H:%M:%S") except Exception as e: raise ValueError(f"日期格式错误,需符合%Y-%m-%d %H:%M:%S:{str(e)}") # 测试场景 # 空字符串转None data1 = {"dt": ""} print(Model.model_validate(data1)) # 输出: dt=None # 合法日期字符串转datetime data2 = {"dt": "2024-05-20 12:30:00"} print(Model.model_validate(data2)) # 输出: dt=datetime.datetime(2024, 5, 20, 12, 30) # 直接传入None data3 = {"dt": None} print(Model.model_validate(data3)) # 输出: dt=None # 非法格式测试 try: Model.model_validate({"dt": "2024-05-20"}) except ValidationError as e: print(e)
方式二:调整Annotated类型包含None
将 Union[datetime, None] 纳入Annotated的范围,让验证器处理所有可能的输入类型:
from pydantic import BaseModel, ValidationError, BeforeValidator from datetime import datetime from typing import Annotated, Union def parse_datetime(value: Union[str, datetime, None]): if value is None: return None if isinstance(value, datetime): return value if len(value.strip()) == 0: return None try: return datetime.strptime(value, "%Y-%m-%d %H:%M:%S") except Exception as e: raise ValueError(f"日期格式错误,需符合%Y-%m-%d %H:%M:%S:{str(e)}") # 调整Annotated包含Union类型 date_time = Annotated[Union[datetime, None], BeforeValidator(parse_datetime)] class Model(BaseModel): dt: date_time # 测试场景 print(Model.model_validate({"dt": ""})) # dt=None print(Model.model_validate({"dt": "2024-05-20 12:30:00"})) # dt=datetime.datetime(2024,5,20,12,30) print(Model.model_validate({"dt": None})) # dt=None
说明
两种方式都能实现需求,其中字段级验证器(方式一)更直观,逻辑集中在字段定义处;方式二则适合需要复用该日期解析逻辑到多个字段的场景。
内容的提问来源于stack exchange,提问作者Ray Chan
相关产品推荐
相关产品推荐

