Pydantic递归实例化模型时如何向子模型传递私有参数
实现方案
核心思路是重写Pydantic提供的实例化后回调钩子,该钩子会在Pydantic完成所有字段的解析、验证、赋值后触发,此时外层ThingList的_api已经赋值完成,内层Thing实例也已经全部创建完成,直接遍历赋值即可。
Pydantic V2 实现
V2官方推荐使用model_post_init作为实例化后自定义操作的钩子,实现代码如下:
from pydantic import BaseModel, PrivateAttr from typing import List class Thing(BaseModel): _api: Api = PrivateAttr() id: int name: str class ThingList(BaseModel): _api: API = PrivateAttr() __root__: List[Thing] def model_post_init(self, __context) -> None: # 遍历所有内层Thing实例,传递_api参数 for thing in self.__root__: thing._api = self._api
Pydantic V1 实现
V1对应的钩子方法是__post_init__,实现逻辑完全一致:
from pydantic import BaseModel, PrivateAttr from typing import List class Thing(BaseModel): _api: Api = PrivateAttr() id: int name: str class ThingList(BaseModel): _api: API = PrivateAttr() __root__: List[Thing] def __post_init__(self): # 遍历所有内层Thing实例,传递_api参数 for thing in self.__root__: thing._api = self._api
方案优势
- 无需修改原有实例化逻辑,原来的
ThingList(__root__=response, api=api)调用可以完全保留 - 不依赖全局变量,符合编码规范
- 完全保留Pydantic原生的字段解析、验证逻辑,后续ThingList新增其他关联属性也不会受影响
- 可扩展性强,可以把传递逻辑抽成通用基类,所有需要传递
_api的嵌套模型都可以继承复用:
# 通用基类示例 class ApiNestedBase(BaseModel): _api: API = PrivateAttr() def model_post_init(self, __context) -> None: # 支持__root__列表类型的属性传递 if hasattr(self, "__root__") and isinstance(self.__root__, list): for item in self.__root__: if isinstance(item, BaseModel) and hasattr(item, "_api"): item._api = self._api # 支持普通嵌套模型字段的属性传递 for field_name in self.model_fields: field_value = getattr(self, field_name) if isinstance(field_value, BaseModel) and hasattr(field_value, "_api"): field_value._api = self._api
内容的提问来源于stack exchange,提问作者Gloweye
相关产品推荐
相关产品推荐

