如何用FastAPI返回包含多种Pydantic模型列表的响应?
FastAPI多类型节点响应去冗余字段解决方案
核心问题原因
FastAPI依赖Pydantic序列化Union类型时,默认会包含所有子模型的字段,未赋值的字段会以null返回,这就是你遇到PersonNode携带SchoolNode的code字段(值为null)的原因。
分步解决方案
1. 正确定义带鉴别器的节点模型
首先要让Pydantic能精准识别节点类型,需要基于带鉴别器的基类定义所有节点模型,每个子类固定label值作为类型标识:
from pydantic import BaseModel, Field from typing import List, Union # 基类:定义鉴别器字段label class BaseNode(BaseModel): id: str label: str = Field(..., discriminator="label") # 各子类固定label值,只声明自身专属字段 class SchoolNode(BaseNode): label: str = "school" code: str class PersonNode(BaseNode): label: str = "person" name: str surname: str class PetNode(BaseNode): label: str = "pet" pet_name: str species: str # 响应模型保持原有结构 class PathResponse(BaseModel): nodes: List[Union[SchoolNode, PersonNode, PetNode]] edges: List[dict] # 按实际关系字段定义
2. 序列化时排除未设置字段
在路由返回响应时,调用model_dump(exclude_unset=True),只保留当前节点实例实际赋值的字段,过滤掉其他类型的冗余字段:
from fastapi import FastAPI app = FastAPI() @app.get("/shortest-path", response_model=PathResponse) def get_shortest_path(): # 模拟业务逻辑生成的节点数据 person_node = PersonNode(id="p001", name="Alice", surname="Smith") school_node = SchoolNode(id="s001", code="SCH-2024") pet_node = PetNode(id="t001", pet_name="Milo", species="Cat") response = PathResponse( nodes=[person_node, school_node, pet_node], edges=[{"source": "p001", "target": "s001"}] ) # 关键:用exclude_unset=True过滤冗余字段 return response.model_dump(exclude_unset=True)
3. 适配ORM模型场景(如果从数据库取数)
如果节点数据来自ORM模型(比如SQLAlchemy),需要在BaseNode中添加配置,确保从ORM实例转换时只取对应字段:
class BaseNode(BaseModel): id: str label: str = Field(..., discriminator="label") class Config: from_attributes = True # 从ORM实例自动映射字段
4. 全局自定义编码器(可选)
如果多个路由都需要处理节点序列化,可以全局配置JSON编码器,避免重复代码:
from fastapi.encoders import jsonable_encoder def node_encoder(node: BaseNode): return node.model_dump(exclude_unset=True) app = FastAPI(json_encoders={BaseNode: node_encoder})
验证效果
调整后,PersonNode的响应只会包含id、label、name、surname字段,不会出现code这类其他节点的冗余字段,完全适配d3js自定义库的格式要求。
内容的提问来源于stack exchange,提问作者ndricca
相关产品推荐
相关产品推荐

