Pydantic父类类型字段传入子类实例时model_dump()无法获取数据
问题描述
我想用自定义__repr__方法生成对象的易读输出,同时希望将字段类型声明为父类,确保传入该字段的所有子类实例都能通过验证。但发现只有在字段定义中显式声明为子类类型时,repr方法里的self.model_dump()才能正常获取子类数据;如果用父类类型声明字段,model_dump()只会返回空字典,无法拿到子类的字段内容。
可正常运行代码(显式声明字段类型)
from pydantic import BaseModel, Field from pydantic.config import ConfigDict class QueryParams(BaseModel): pass class subQueryParams(QueryParams): test: str = "test" class YourModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) command_params: subQueryParams = Field() def __repr__(self) -> str: """Human readable representation of the object.""" items = [ f"{k}: {v}"[:83] + ("..." if len(f"{k}: {v}") > 83 else "") for k, v in self.model_dump().items() ] return f"{self.__class__.__name__}\n\n" + "\n".join(items) YourModel(command_params=subQueryParams())
返回结果:
YourModel command_params: {'test': 'test'}
异常代码(字段类型改为父类)
from pydantic import BaseModel, Field from pydantic.config import ConfigDict class QueryParams(BaseModel): pass class subQueryParams(QueryParams): test: str = "test" class YourModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) command_params: QueryParams = Field() def __repr__(self) -> str: """Human readable representation of the object.""" items = [ f"{k}: {v}"[:83] + ("..." if len(f"{k}: {v}") > 83 else "") for k, v in self.model_dump().items() ] return f"{self.__class__.__name__}\n\n" + "\n".join(items) YourModel(command_params=subQueryParams())
返回结果:
YourModel command_params: {}
问题
如何在使用父类类型声明字段的同时,得到第一个代码示例中的理想输出?
临时解决方案
def __repr__(self) -> str: """Human readable representation of the object.""" items = [ f"{k}: {v}"[:83] + ("..." if len(f"{k}: {v}") > 83 else "") for k, v in self.model_dump().items() ] # 提取子类数据 if self.command_params: add_item = self.command_params.model_dump() for i, item in enumerate(items): if item.startswith('command_params:'): items[i] = f'command_params: {add_item}' break # 假设只有一个command_params字段,更新后停止 return f"{self.__class__.__name__}\n\n" + "\n".join(items)
最优解决方案
方法1:修改父类配置,允许额外字段
在父类QueryParams中添加extra='allow'配置,让父类可以接受并序列化子类的所有额外字段:
from pydantic import BaseModel, Field from pydantic.config import ConfigDict class QueryParams(BaseModel): model_config = ConfigDict(extra='allow') class subQueryParams(QueryParams): test: str = "test" class YourModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) command_params: QueryParams = Field() def __repr__(self) -> str: items = [ f"{k}: {v}"[:83] + ("..." if len(f"{k}: {v}") > 83 else "") for k, v in self.model_dump().items() ] return f"{self.__class__.__name__}\n\n" + "\n".join(items) # 运行测试 print(YourModel(command_params=subQueryParams()))
运行后会输出:
YourModel command_params: {'test': 'test'}
方法2:直接序列化实际对象
如果不想修改父类配置,可以在__repr__中直接对字段的实际对象进行序列化,而不是依赖self.model_dump()的结果:
from pydantic import BaseModel, Field from pydantic.config import ConfigDict class QueryParams(BaseModel): pass class subQueryParams(QueryParams): test: str = "test" class YourModel(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) command_params: QueryParams = Field() def __repr__(self) -> str: items = [] for attr_name, attr_value in self.__dict__.items(): # 对Pydantic模型对象单独序列化 if isinstance(attr_value, BaseModel): serialized_value = attr_value.model_dump() else: serialized_value = attr_value item_str = f"{attr_name}: {serialized_value}" # 处理过长内容 if len(item_str) > 83: item_str = item_str[:83] + "..." items.append(item_str) return f"{self.__class__.__name__}\n\n" + "\n".join(items) # 运行测试 print(YourModel(command_params=subQueryParams()))
这种方法不受字段声明类型的限制,会直接序列化对象的实际内容,同样能得到正确结果。
内容的提问来源于stack exchange,提问作者JJ Fantini
相关产品推荐
相关产品推荐

