如何在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
相关产品推荐
相关产品推荐

