FastAPI + Pydantic v2下如何在继承模型中处理dd/mm/yyyy格式的查询参数日期
FastAPI + Pydantic v2下如何在继承模型中处理dd/mm/yyyy格式的查询参数日期
你遇到的核心问题是:Pydantic v2对date类型的默认解析仅支持ISO格式(如2025-10-09),且默认解析逻辑会在你自定义的验证器之前触发,导致输入dd/mm/yyyy格式时直接报错。下面提供两种优雅的解决方案,完全满足你保留类型安全、支持模型继承、OpenAPI schema正确的需求。
方案1:使用Annotated + BeforeValidator(快速实现)
这种方法通过Annotated给原生date类型绑定前置解析逻辑,既保留date类型的所有特性,又能处理自定义格式,代码简洁易维护。
完整代码示例
from datetime import date, datetime from pydantic import BaseModel, Field, BeforeValidator, PlainSerializer from typing import Annotated from fastapi import FastAPI, Depends app = FastAPI() # 1. 定义dd/mm/yyyy格式的日期解析函数 def parse_ddmmyyyy_date(v: str) -> date: if isinstance(v, str): try: return datetime.strptime(v, "%d/%m/%Y").date() except ValueError as e: raise ValueError(f"无效的日期格式,请传入 dd/mm/yyyy 格式(例如:09/10/2025)") from e # 若已为date对象,直接返回(兼容子类手动传入date的场景) return v # 2. 封装成可复用的Annotated类型 DdMmYyyyDate = Annotated[ date, BeforeValidator(parse_ddmmyyyy_date), # 可选:序列化时返回ISO格式(若需返回dd/mm/yyyy,可改为lambda d: d.strftime("%d/%m/%Y")) PlainSerializer(lambda d: d.isoformat(), return_type=str) ] # 3. 基础Webhook模型(子类可直接继承) class BaseWebhookModel(BaseModel): date_field: DdMmYyyyDate = Field(alias="Date") # 4. 子类继承扩展模型 class WebhookModel(BaseWebhookModel): extra_field: str = Field(alias="ExtraField") status: str = Field(alias="Status") # 5. 接口端点 @app.get("/test") def test_api(param: WebhookModel = Depends()): return { "received_date": param.date_field, "extra_field": param.extra_field, "status": param.status }
方案2:自定义Date类型(全局复用更灵活)
如果你的项目中多个模型都需要处理该日期格式,自定义一个继承自date的类型会更干净,Pydantic会自动识别它为date类型,OpenAPI schema也会正确显示。
完整代码示例
from datetime import date, datetime from pydantic import BaseModel, Field from pydantic_core import core_schema from pydantic import GetCoreSchemaHandler from typing import Any, Type from fastapi import FastAPI, Depends app = FastAPI() # 1. 自定义支持dd/mm/yyyy格式的Date类型 class DdMmYyyyDate(date): @classmethod def __get_pydantic_core_schema__( cls, source_type: Any, handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: # 定义解析逻辑 def parse_date(v: Any) -> date: if isinstance(v, str): try: return datetime.strptime(v, "%d/%m/%Y").date() except ValueError as e: raise ValueError(f"无效的日期格式,请传入 dd/mm/yyyy 格式(例如:09/10/2025)") from e elif isinstance(v, date): return v else: raise ValueError(f"无法解析为日期:{v}") # 核心Schema:基于原生date的Schema,添加自定义解析逻辑 return core_schema.no_info_wrap_validator_function( parse_date, core_schema.date_schema(), # 告诉Pydantic这是date类型,OpenAPI会显示为date serialization=core_schema.plain_serializer_function_ser_schema( lambda d: d.isoformat() # 序列化时返回ISO格式 ), ) # 2. 基础Webhook模型 class BaseWebhookModel(BaseModel): date_field: DdMmYyyyDate = Field(alias="Date") # 3. 子类继承扩展模型 class WebhookModel(BaseWebhookModel): extra_field: str = Field(alias="ExtraField") status: str = Field(alias="Status") # 4. 接口端点 @app.get("/test") def test_api(param: WebhookModel = Depends()): return { "received_date": param.date_field, "extra_field": param.extra_field, "status": param.status }
方案优势(完全匹配你的需求)
- 支持dd/mm/yyyy格式:自定义解析逻辑专门处理该格式,自动转换为
date对象。 - 保留类型与OpenAPI正确性:两种方案都基于原生
date类型,OpenAPI schema会正确显示为date类型,而非自定义类型。 - 无缝支持模型继承:子类直接继承
BaseWebhookModel的date_field,无需重复编写解析逻辑。 - 无需手动解析端点:模型自动处理查询参数的解析和验证,端点只需依赖模型即可。
- 友好的错误提示:自定义错误信息明确告知正确格式,替代默认的ISO格式提示,更符合你的场景。
测试验证
- 正确请求:
/test?Date=09/10/2025&ExtraField=foo&Status=bar
返回结果:{ "received_date": "2025-10-09", "extra_field": "foo", "status": "bar" } - 错误格式请求:
/test?Date=09-10-2025&ExtraField=foo&Status=bar
返回友好错误:{ "detail": [ { "type": "value_error", "loc": ["query", "Date"], "msg": "无效的日期格式,请传入 dd/mm/yyyy 格式(例如:09/10/2025)", "input": "09-10-2025" } ] }




