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

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
    }

方案优势(完全匹配你的需求)

  1. 支持dd/mm/yyyy格式:自定义解析逻辑专门处理该格式,自动转换为date对象。
  2. 保留类型与OpenAPI正确性:两种方案都基于原生date类型,OpenAPI schema会正确显示为date类型,而非自定义类型。
  3. 无缝支持模型继承:子类直接继承BaseWebhookModeldate_field,无需重复编写解析逻辑。
  4. 无需手动解析端点:模型自动处理查询参数的解析和验证,端点只需依赖模型即可。
  5. 友好的错误提示:自定义错误信息明确告知正确格式,替代默认的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"
        }
      ]
    }
    

火山引擎 最新活动