自定义Pydantic模型作为FastAPI的response_model时OpenAPI Schema未生成组件引用的问题咨询
我最近在开发一个基础代码库,里面既有传统的继承自BaseModel的Pydantic模型,也有通过实现__get_pydantic_core_schema__和__get_pydantic_json_schema__来定义的自定义模型。但当我把这种自定义模型用作FastAPI路由的response_model时,生成的OpenAPI规范并没有把它作为组件模型生成引用,而是直接把结构内联在响应里了。我想知道正确使用这类自定义模型和FastAPI配合的方式是什么?
我的代码示例如下:
from pydantic_core import core_schema from typing import Any from fastapi.routing import APIRouter from pydantic.json_schema import GenerateJsonSchema router = APIRouter() class _CustomModel: def __init__(self, id: int, name: str) -> None: self.id = id self.name = name @classmethod def _validate(cls, v): return v @staticmethod def _make_schema(): mfs = {} mfs["id"] = core_schema.model_field(schema=core_schema.int_schema()) mfs["name"] = core_schema.model_field(schema=core_schema.str_schema()) schema = core_schema.model_fields_schema(mfs) return schema @classmethod def __get_pydantic_core_schema__(cls, source_type: Any, handler): cls._schema = _CustomModel._make_schema() fn = core_schema.no_info_after_validator_function( function=cls._validate, schema=cls._schema, ) return fn @classmethod def __get_pydantic_json_schema__(cls, core_schema, handler): # TODO: here generator = GenerateJsonSchema() json_schema = generator.generate(cls._schema, mode="validation") return handler.resolve_ref_schema(json_schema) @router.get( "/", summary="stub", response_model=_CustomModel, ) def get_stub(): response = _CustomModel(1, "Martin") return response
问题分析
你的自定义模型实现有几个关键问题导致FastAPI无法生成可复用的OpenAPI组件引用:
核心Schema类型不被识别:你在
__get_pydantic_core_schema__中返回的no_info_after_validator_function属于"验证器包装器"类型,Pydantic和FastAPI不会将其识别为可复用的模型Schema,因此不会将其提取到OpenAPI的components/schemas中。JSON Schema生成逻辑绕开了默认注册流程:你直接实例化
GenerateJsonSchema生成Schema,而没有使用Pydantic提供的handler来处理核心Schema,导致Pydantic无法将该Schema注册为可复用的命名组件,只能返回内联结构。
解决方案
要让自定义模型被FastAPI正确识别为可复用的Schema组件,需要调整核心Schema的定义方式和JSON Schema的处理逻辑,让Pydantic能够将其注册为命名模型:
修改后的完整代码
from pydantic_core import core_schema from typing import Any from fastapi.routing import APIRouter from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler from pydantic.json_schema import JsonSchemaValue router = APIRouter() class _CustomModel: def __init__(self, id: int, name: str) -> None: self.id = id self.name = name @classmethod def _validate(cls, v): # 处理两种场景:直接传入模型实例,或传入字典(FastAPI序列化/反序列化时) if isinstance(v, dict): return cls(v["id"], v["name"]) return v @classmethod def __get_pydantic_core_schema__( cls, source_type: Any, handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: # 使用core_schema.model定义标准模型Schema,标记为可命名组件 model_core_schema = core_schema.model( fields={ "id": core_schema.int_schema(), "name": core_schema.str_schema(), }, # 指定模型的显示名称,会出现在OpenAPI components/schemas中 metadata={"title": "CustomModel"} ) # 用验证器包装模型Schema,确保输入被转换为_CustomModel实例 return core_schema.no_info_after_validator_function( function=cls._validate, schema=model_core_schema, ) @classmethod def __get_pydantic_json_schema__( cls, core_schema_: core_schema.CoreSchema, handler: GetJsonSchemaHandler ) -> JsonSchemaValue: # 让Pydantic的默认handler处理核心Schema,自动完成组件注册 json_schema = handler(core_schema_) # 自定义模型的显示名称(可选,若metadata中已指定title可省略) json_schema["title"] = "CustomModel" # 解析为引用Schema,而不是内联结构 return handler.resolve_ref_schema(json_schema) @router.get( "/", summary="stub", response_model=_CustomModel, ) def get_stub(): response = _CustomModel(1, "Martin") return response
关键修改点说明
使用
core_schema.model定义核心Schema:
这是Pydantic推荐的模型Schema定义方式,会被识别为可复用的命名模型类型。通过metadata={"title": "CustomModel"}指定的名称,会直接作为OpenAPI组件的键名。优化验证逻辑:
调整_validate方法,处理字典输入的场景(比如FastAPI在序列化响应时会将模型实例转为字典,再验证回模型类型),确保类型转换的完整性。依赖Pydantic的默认JSON Schema处理流程:
在__get_pydantic_json_schema__中直接调用handler(core_schema_),让Pydantic的默认逻辑处理Schema生成和组件注册,再通过handler.resolve_ref_schema返回引用,而不是内联结构。使用标准类型提示:
引入GetCoreSchemaHandler和GetJsonSchemaHandler类型,让代码更符合Pydantic的规范,同时获得更好的类型检查支持。
验证效果
启动FastAPI应用后,访问/docs或/redoc,你会看到OpenAPI规范的components/schemas中出现了CustomModel,而路由的响应Schema会通过{"$ref": "#/components/schemas/CustomModel"}引用该组件,不再是内联的结构。
内容来源于stack exchange

