You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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']

关键说明

  1. build_extra根验证器:负责收集当前模型层级的额外字段,存入extra字典,同时兼容字段别名的识别逻辑。
  2. extras属性:通过递归遍历模型实例的字段,识别嵌套的NewBase子类实例,自动拼接字段路径,最终返回所有层级的额外字段路径列表。
  3. 扩展性:支持任意深度的嵌套模型,只要嵌套模型继承自NewBase,就能自动收集其内部的额外字段。

内容的提问来源于stack exchange,提问作者AlienDeg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 10:05:22