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

如何在Pydantic中移除所有嵌套模型的指定字段,或实现带类名信息的上下文特定序列化

如何在Pydantic中移除所有嵌套模型的指定字段,或实现带类名信息的上下文特定序列化

我来帮你搞定这两个问题——先解决递归移除所有嵌套模型class_name字段的难题,再实现你想要的带类名的上下文序列化功能。

一、递归移除所有嵌套模型的指定字段

你之前用exclude={"class_name": True, "__all__": {"class_name"}}的写法不对,Pydantic V2里并没有__all__这种语法来实现递归排除字段。这里给你两种靠谱的实现方式:

方法1:自定义递归清理函数

写一个辅助函数,遍历序列化后的字典,递归删除指定字段:

from typing import Dict, Any

def remove_nested_field(data: Dict[str, Any], field_name: str) -> Dict[str, Any]:
    if not isinstance(data, dict):
        return data
    cleaned_data = {}
    for key, value in data.items():
        if key != field_name:
            cleaned_data[key] = remove_nested_field(value, field_name)
    return cleaned_data

# 用法示例
m = Level1(whenever=3.14, level2=Level2(whatever=123, level3=Level3(whatever=20)))
raw_dump = m.model_dump()
cleaned_dump = remove_nested_field(raw_dump, "class_name")
print(cleaned_dump)
# 输出:{'whenever': 3.14, 'level2': {'whatever': 123, 'level3': {'whatever': 20}}}

方法2:在基类重写model_dump自动处理

如果不想每次手动调用辅助函数,可以在基类里重写model_dump,让它支持递归排除指定字段:

class BaseModel2(BaseModel):
    class_name: Optional[str] = Field(None, validate_default=True)

    @field_validator("class_name")
    @classmethod
    def set_class_name(cls, v):
        if v is None:
            return cls.__name__
        else:
            raise ValueError("class_name must not be set")
    
    def model_dump(self, exclude_nested: list = None, **kwargs):
        exclude_nested = exclude_nested or []
        # 先调用父类的序列化逻辑
        data = super().model_dump(**kwargs)
        # 递归清理指定字段
        def clean_dict(data_dict):
            if not isinstance(data_dict, dict):
                return data_dict
            cleaned = {}
            for k, v in data_dict.items():
                if k not in exclude_nested:
                    cleaned[k] = clean_dict(v)
            return cleaned
        return clean_dict(data)

# 用法示例
print(m.model_dump(exclude_nested=["class_name"]))
# 输出和上面的清理结果一致

二、实现带类名信息的上下文特定序列化

你的终极目标是让每个模型序列化时自动带上类名,同时能灵活控制是否显示这个信息。这里给你两个无递归问题的可行方案:

方案1:重写model_dump控制类名字段显示

在基类里重写model_dump,添加类名字段(比如__vizro_model__),并通过参数控制是否包含它:

from typing import Optional, Dict, Any

class BaseModel2(BaseModel):
    def model_dump(self, include_class_name: bool = True, **kwargs):
        # 先获取父类的序列化结果
        data = super().model_dump(**kwargs)
        if include_class_name:
            # 给当前模型添加类名字段
            data["__vizro_model__"] = self.__class__.__name__
        return data

# 子类继承后自动生效
class Level3(BaseModel2):
    whatever: int = 10

class Level2(BaseModel2):
    whatever: int
    level3: Level3

class Level1(BaseModel2):
    whenever: Optional[float] = 1.1
    level2: Level2

# 测试:包含类名
m = Level1(whenever=3.14, level2=Level2(whatever=123, level3=Level3(whatever=20)))
print(m.model_dump())
# 每个嵌套模型都会带上自己的__vizro_model__字段

# 测试:不包含类名(相当于移除所有类名字段)
print(m.model_dump(include_class_name=False))
# 输出不带类名的干净数据

这个方案逻辑简单,完全不会有递归问题——每个模型的model_dump只处理自己的字段,嵌套模型会自动调用自身的model_dump方法,完美实现递归添加类名。

方案2:使用model_serializer正确处理递归

如果你想用model_serializer,关键是要避免手动调用self.model_dump()导致的递归循环,直接通过实例属性和序列化信息处理:

from typing import Dict, Any
from pydantic import model_serializer, SerializationInfo

class BaseModel2(BaseModel):
    @model_serializer
    def serialize_model(self, info: SerializationInfo) -> Dict[str, Any]:
        # 让Pydantic自动处理所有字段(包括嵌套模型),同时过滤掉需要排除的字段
        data = {k: v for k, v in self.__dict__.items() if k not in info.exclude}
        # 添加当前模型的类名字段
        data["__vizro_model__"] = self.__class__.__name__
        return data

# 子类继承后自动生效,测试方式和上面一致

总结

  • 递归移除嵌套字段:要么用辅助函数清理序列化结果,要么在基类重写model_dump自动处理;
  • 带类名的上下文序列化:重写model_dump是最直观且无递归问题的方案,完全能满足你的需求。

备注:内容来源于stack exchange,提问作者GermanExpress

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 17:43:07