如何获取Pydantic模型及其嵌套模型的所有额外字段
获取Pydantic模型及嵌套模型的所有额外字段
需求:给Pydantic模型添加.extras属性,返回自身及所有嵌套模型实例中的未定义额外数据,字段路径采用父字段.子字段的格式,同时支持字段别名。
现有代码尝试实现该功能,但仅能收集当前模型的额外字段,无法处理嵌套模型中的额外数据:
from typing import Any, Dict, Optional from pydantic import BaseModel, Field, root_validator unnecessary_data = { "name": "Lévy", "age": 3, "key_parent": "value", # 额外字段 "key2_parent": "value2", # 额外字段 "address": { "city": "Wonderland", "zip_code": "ABCDE", "number": 123, "key_child": 1232 # 嵌套模型中的额外字段 } } class NewBase(BaseModel): versio: Optional[str] = Field(alias='version') # 演示别名支持 extra: Dict[str, Any] @root_validator(pre=True) def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]: all_required_field_names = {field.alias for field in cls.__fields__.values() if field.alias != 'extra'} # 支持别名 extra: Dict[str, Any] = {} # 修正原代码缩进错误 for field_name in list(values): if field_name not in all_required_field_names: extra[field_name] = values.pop(field_name) values['extra'] = extra return values class Address(NewBase): """ Cat API Address定义 """ city: str zip_code: str number: int class CatRequest(NewBase): """ Cat API Request定义 """ name: str age: int address: Address validated = CatRequest(**unnecessary_data) # 期望输出: ["key_parent", "key2_parent", "address.key_child"] print(validated.extras)
解决方案
给NewBase添加递归收集逻辑,通过@property实现.extras属性,自动遍历所有嵌套模型并收集额外字段路径:
from typing import Any, Dict, Optional, Iterator from pydantic import BaseModel, Field, root_validator unnecessary_data = { "name": "Lévy", "age": 3, "key_parent": "value", "key2_parent": "value2", "address": { "city": "Wonderland", "zip_code": "ABCDE", "number": 123, "key_child": 1232 } } class NewBase(BaseModel): versio: Optional[str] = Field(alias='version') extra: Dict[str, Any] = {} @root_validator(pre=True) def build_extra(cls, values: Dict[str, Any]) -> Dict[str, Any]: # 获取当前模型所有字段的别名(排除extra字段) all_field_aliases = {field.alias for field in cls.__fields__.values() if field.alias != 'extra'} extra = {} for field_name in list(values): if field_name not in all_field_aliases: extra[field_name] = values.pop(field_name) values['extra'] = extra return values @property def extras(self) -> list[str]: """递归收集所有层级的额外字段路径""" def _collect_extras(obj: BaseModel, prefix: str = "") -> Iterator[str]: # 收集当前层级的额外字段 for key in obj.extra: yield f"{prefix}.{key}" if prefix else key # 遍历字段,处理嵌套的NewBase实例 for field_name, field_value in obj.__dict__.items(): if field_name == "extra": continue if isinstance(field_value, NewBase): new_prefix = f"{prefix}.{field_name}" if prefix else field_name yield from _collect_extras(field_value, new_prefix) return list(_collect_extras(self)) class Address(NewBase): city: str zip_code: str number: int class CatRequest(NewBase): name: str age: int address: Address validated = CatRequest(**unnecessary_data) print(validated.extras) # 输出: ['key_parent', 'key2_parent', 'address.key_child']
关键说明
build_extra根验证器:负责收集当前模型层级的额外字段,存入extra字典,同时兼容字段别名的识别逻辑。extras属性:通过递归遍历模型实例的字段,识别嵌套的NewBase子类实例,自动拼接字段路径,最终返回所有层级的额外字段路径列表。- 扩展性:支持任意深度的嵌套模型,只要嵌套模型继承自
NewBase,就能自动收集其内部的额外字段。
内容的提问来源于stack exchange,提问作者AlienDeg
相关产品推荐
相关产品推荐

