如何在FastAPI响应中包含非pydantic类型的自定义类
解决方案
你遇到的报错本质是Pydantic默认不知道如何序列化没有继承BaseModel的自定义Foo类,不需要修改Foo的继承关系,通过以下两种鸭子类型的适配方案即可实现需求:
方案1:可修改Foo类源码时直接添加Pydantic识别的魔法方法
Pydantic V2会识别类上的__get_pydantic_core_schema__静态方法,只要实现该方法定义序列化逻辑,不需要继承BaseModel即可被Pydantic正常处理。
完整可运行代码如下:
from fastapi import FastAPI from pydantic import BaseModel from pydantic_core import core_schema app = FastAPI() class Foo: """Foo holds data and can't inherit from `pydantic.BaseModel`.""" def __init__(self, x: int): self.x = x @staticmethod def __get_pydantic_core_schema__(cls, _source_type, _handler): # 定义Foo实例的序列化逻辑 def serialize_foo(foo_instance: Foo): return {"x": foo_instance.x} return core_schema.any_schema( serialization=core_schema.plain_serializer_function_ser_func(serialize_foo) ) class Response(BaseModel): foo: Foo model_config = { "arbitrary_types_allowed": True } @app.get("/", response_model=Response) def root(): return Response(foo=Foo(1)) if __name__ == '__main__': import uvicorn uvicorn.run("main:app")
方案2:无法修改Foo类源码时自定义适配类型
如果Foo来自第三方库无法修改源码,可以通过自定义注解类型实现适配,完全不侵入原有Foo类的代码:
from typing import Annotated from fastapi import FastAPI from pydantic import BaseModel, GetCoreSchemaHandler from pydantic_core import core_schema app = FastAPI() class Foo: """Foo holds data and can't inherit from `pydantic.BaseModel`.""" def __init__(self, x: int): self.x = x # 自定义序列化逻辑 def serialize_foo(foo_instance: Foo): return {"x": foo_instance.x} # 自定义适配注解 class _FooPydanticAnnotation: @classmethod def __get_pydantic_core_schema__(cls, _source_type, _handler: GetCoreSchemaHandler): return core_schema.any_schema( serialization=core_schema.plain_serializer_function_ser_func(serialize_foo) ) FooType = Annotated[Foo, _FooPydanticAnnotation] class Response(BaseModel): foo: FooType model_config = { "arbitrary_types_allowed": True } @app.get("/", response_model=Response) def root(): return Response(foo=Foo(1)) if __name__ == '__main__': import uvicorn uvicorn.run("main:app")
Pydantic V1 兼容方案
如果你使用的是Pydantic V1,不需要额外加魔法方法,直接在响应模型的配置中添加任意类型允许和自定义序列化规则即可:
class Response(BaseModel): foo: Foo class Config: arbitrary_types_allowed = True json_encoders = { Foo: lambda x: {"x": x.x} }
内容的提问来源于stack exchange,提问作者Arne
相关产品推荐
相关产品推荐

