Pydantic v2中如何用model_dump全局排除嵌套模型的计算字段?
解决Pydantic v2嵌套模型中全局排除computed字段的问题
问题复现
在Pydantic v2中,调用model_dump(exclude={'computed': True})时,仅根模型的computed计算字段会被排除,嵌套子模型中的同名字段仍会被序列化输出,示例代码如下:
from pydantic import BaseModel, computed_field class SubSystem(BaseModel): value: int @computed_field def computed(self) -> int: return self.value * 2 class System(BaseModel): sub: SubSystem value: int @computed_field def computed(self) -> int: return self.value * 3 system = System(sub=SubSystem(value=10), value=20) # 仅排除根模型的computed,子模型的computed仍保留 print(system.model_dump(exclude={'computed': True})) # 输出: {'sub': {'value': 10, 'computed': 20}, 'value': 20}
解决方案
方法1:使用自定义排除函数(推荐)
Pydantic v2的model_dump方法支持传入函数类型的exclude参数,该函数会遍历所有层级的字段,判断是否需要排除。我们可以定义一个函数,只要字段名是computed就返回True,从而全局排除所有层级的该字段,同时保留exclude_defaults、by_alias等原有参数的功能:
from typing import Any from pydantic import BaseModel, computed_field # 定义排除函数 def exclude_all_computed(field_name: str, _: Any) -> bool: return field_name == 'computed' # 模型定义不变 class SubSystem(BaseModel): value: int @computed_field def computed(self) -> int: return self.value * 2 class System(BaseModel): sub: SubSystem value: int @computed_field def computed(self) -> int: return self.value * 3 system = System(sub=SubSystem(value=10), value=20) # 调用时传入排除函数,同时可添加其他参数 result = system.model_dump( exclude=exclude_all_computed, exclude_defaults=True, by_alias=False ) print(result) # 输出: {'sub': {'value': 10}, 'value': 20}
方法2:自定义BaseModel统一处理
如果你的项目中有大量模型需要全局排除computed字段,可以自定义一个基础模型类,重写model_dump方法,默认添加全局排除computed的逻辑,同时兼容原有参数:
from typing import Any, Dict, Optional from pydantic import BaseModel, computed_field class CustomBaseModel(BaseModel): def model_dump( self, *, mode: str = 'python', include: Optional[Dict[str, Any]] = None, exclude: Optional[Dict[str, Any] | callable] = None, by_alias: bool = False, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, round_trip: bool = False, warnings: bool = True, ) -> Dict[str, Any]: # 组合排除规则:优先排除computed,再应用用户传入的exclude规则 def combined_exclude(field_name: str, value: Any) -> bool: if field_name == 'computed': return True # 处理用户传入的exclude规则 if callable(exclude): return exclude(field_name, value) elif isinstance(exclude, dict): return field_name in exclude return False final_exclude = combined_exclude if exclude is not None else (lambda fn, _: fn == 'computed') return super().model_dump( mode=mode, include=include, exclude=final_exclude, by_alias=by_alias, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, round_trip=round_trip, warnings=warnings, ) # 继承自定义基础模型 class SubSystem(CustomBaseModel): value: int @computed_field def computed(self) -> int: return self.value * 2 class System(CustomBaseModel): sub: SubSystem value: int @computed_field def computed(self) -> int: return self.value * 3 system = System(sub=SubSystem(value=10), value=20) # 直接调用model_dump即可全局排除computed,同时支持其他参数 result = system.model_dump(exclude_defaults=True) print(result) # 输出: {'sub': {'value': 10}, 'value': 20}
说明
- 方法1更灵活,适合临时或局部场景使用;
- 方法2适合全局统一配置,减少重复代码;
- 两种方案都完全保留了
model_dump原生参数的功能,不会影响exclude_defaults、by_alias等参数的正常使用。
内容的提问来源于stack exchange,提问作者Maxim Suslov
相关产品推荐
相关产品推荐

