Pydantic v2自定义响应模型出现Could not resolve reference错误的解决
Pydantic v2 + FastAPI 自定义类型导致Schema解析错误的解决方法
问题场景
在pydantic==1.10.9中,以下代码可正常生成FastAPI的/docs文档,但升级到pydantic==2.3.0后出现Resolver错误,提示无法解析components引用。将自定义的Hour类型改为int则恢复正常,问题与自定义类型的Schema生成逻辑相关。
示例代码
import pydantic from fastapi import FastAPI class Hour(int): def __new__(cls, *args, **kwargs): h = super().__new__(cls, *args, **kwargs) assert 0 <= h < 24, f"Invalid hour: {h}" return h class PydanticConfig: arbitrary_types_allowed = True @pydantic.dataclasses.dataclass(frozen=True, config=PydanticConfig) class Time: hour: Hour app = FastAPI() @app.get("/", response_model=Time) def read_root(): return Time(13)
错误信息
Errors Resolver error at responses.200.content.application/json.schema.$ref Could not resolve reference: Evaluation failed on token: "components" Resolver error at responses.200.content.application/json.schema.$ref Could not resolve reference: Evaluation failed on token: "components"
解决方法
Pydantic v2对自定义类型的Schema生成逻辑做了调整,需要给自定义类型显式指定JSON Schema规则,以下是三种可行方案:
方案1:使用Annotated绑定序列化与Schema规则(推荐)
通过Annotated给Hour类型绑定序列化逻辑和JSON Schema约束,让Pydantic能正确识别类型的Schema:
import pydantic from fastapi import FastAPI from pydantic import PlainSerializer, WithJsonSchema from typing import Annotated class Hour(int): def __new__(cls, *args, **kwargs): h = super().__new__(cls, *args, **kwargs) assert 0 <= h < 24, f"Invalid hour: {h}" return h # 给Hour类型绑定JSON Schema与序列化规则 HourSchema = Annotated[ Hour, PlainSerializer(lambda x: int(x), return_type=int), WithJsonSchema({"type": "integer", "minimum": 0, "maximum": 23}) ] class PydanticConfig: arbitrary_types_allowed = True @pydantic.dataclasses.dataclass(frozen=True, config=PydanticConfig) class Time: hour: HourSchema # 使用绑定规则后的类型 app = FastAPI() @app.get("/", response_model=Time) def read_root(): return Time(13)
方案2:给自定义类型添加Schema生成方法
直接在Hour类中实现Pydantic v2要求的__get_pydantic_json_schema__方法,让类型自身提供正确的Schema:
import pydantic from fastapi import FastAPI from pydantic.json_schema import GenerateJsonSchema class Hour(int): def __new__(cls, *args, **kwargs): h = super().__new__(cls, *args, **kwargs) assert 0 <= h < 24, f"Invalid hour: {h}" return h @classmethod def __get_pydantic_json_schema__(cls, schema_generator: GenerateJsonSchema, field_schema): # 复用int类型的Schema模板,添加自定义范围约束 int_schema = schema_generator.generate_schema(int) int_schema.update({ "minimum": 0, "maximum": 23 }) return int_schema class PydanticConfig: arbitrary_types_allowed = True @pydantic.dataclasses.dataclass(frozen=True, config=PydanticConfig) class Time: hour: Hour app = FastAPI() @app.get("/", response_model=Time) def read_root(): return Time(13)
方案3:改用Pydantic BaseModel替代dataclass
如果可以调整数据结构定义方式,改用BaseModel配合字段验证器,也能正常生成Schema:
from fastapi import FastAPI from pydantic import BaseModel, field_validator class Hour(int): def __new__(cls, *args, **kwargs): h = super().__new__(cls, *args, **kwargs) assert 0 <= h < 24, f"Invalid hour: {h}" return h class Time(BaseModel): hour: Hour @field_validator('hour') def check_hour_range(cls, v): assert 0 <= v < 24, f"Invalid hour: {v}" return v app = FastAPI() @app.get("/", response_model=Time) def read_root(): return Time(hour=13)
内容的提问来源于stack exchange,提问作者capitalistcuttle
相关产品推荐
相关产品推荐

