如何将嵌套Pydantic模型序列化为扁平JSON?附疑问
解决方案与疑问解答
一、实现扁平化序列化(无需重复定义字段)
方案1:自定义扁平化函数处理Pydantic模型输出
直接在序列化阶段对model_dump()的结果进行递归扁平化,自动给嵌套字段添加父字段前缀,无需修改原模型定义:
from pydantic import BaseModel class StaffMember(BaseModel): fname: str lname: str id: int class CandidateResponse(BaseModel): id: int content: str created_by: StaffMember updated_by: StaffMember def flatten_model_data(model_instance, parent_key="", sep="_"): flattened = {} for key, value in model_instance.model_dump().items(): new_key = f"{parent_key}{sep}{key}" if parent_key else key if isinstance(value, dict): # 递归处理嵌套字典 flattened.update(flatten_model_data(value, new_key, sep)) else: flattened[new_key] = value return flattened # 示例使用 staff = StaffMember(fname="Alice", lname="Smith", id=2) candidate = CandidateResponse(id=50, content="面试通过", created_by=staff, updated_by=staff) flat_json = flatten_model_data(candidate)
输出的扁平结构示例:
{ "id": 50, "content": "面试通过", "created_by_fname": "Alice", "created_by_lname": "Smith", "created_by_id": 2, "updated_by_fname": "Alice", "updated_by_lname": "Smith", "updated_by_id": 2 }
方案2:SQLAlchemy查询阶段直接扁平化
如果数据来自SQLAlchemy查询,可以在查询时通过label给关联字段添加前缀,直接返回扁平结果,再映射到Pydantic模型(适合不需要保留嵌套模型的场景):
from sqlalchemy import select # 假设SQLAlchemy模型为Staff和Candidate query = select( Candidate.id, Candidate.content, Staff.fname.label("created_by_fname"), Staff.lname.label("created_by_lname"), Staff.id.label("created_by_id"), # 同理处理updated_by关联的Staff字段 ).join(Staff, Candidate.created_by_id == Staff.id) # 查询结果直接是扁平结构,可直接传入扁平版Pydantic模型
二、疑问解答
1. 返回扁平对象给API消费者是否恰当?
取决于具体场景:
- 适合场景:如果消费者是前端、移动端等简单客户端,扁平结构无需深层嵌套取值,降低前端处理复杂度;如果已经和消费者约定了扁平格式,按约定实现即可。
- 需要谨慎的场景:如果是面向复杂系统集成的API,嵌套结构更能清晰体现数据的关联语义,扩展性更强(比如StaffMember新增字段时,嵌套结构只需在子对象中扩展,不会污染顶层字段)。
建议先和API消费者对齐需求,评估双方的维护和使用成本后再决定。
2. 是否有其他更合适的实现方案?
除上述方案外,还可以:
- 利用Pydantic v2+的
@computed_field:逐个定义扁平字段,复用嵌套模型的属性(适合仅需部分字段扁平化的场景,但需要手动定义每个字段)。 - 自定义Pydantic模型的
json()方法:重写序列化逻辑,在模型内部实现扁平化,对外提供统一的序列化入口。
内容的提问来源于stack exchange,提问作者5.9 protons
相关产品推荐
相关产品推荐

