如何递归遍历Pydantic模型并对字符串字段执行预处理?
递归预处理Pydantic模型及嵌套结构的解决方案
原代码存在的问题
- 函数未添加返回语句,调用后会返回
None而非处理后的对象 - 遍历Pydantic模型的方式错误,直接迭代模型实例会获取到非业务字段的属性
- 参数类型注解过于严格,无法处理字符串、列表等非BaseModel类型
- 可迭代对象的处理逻辑不完善,未考虑容器内的字符串元素,且未区分可变/不可变容器
修正后的代码
from collections.abc import Iterable from pydantic import BaseModel from typing import Any def _remove_html_tag(s: str) -> str: # 替换为你的实际预处理逻辑 return s.replace("<", "").replace(">", "") def preprocess_item(item: Any) -> Any: # 处理字符串:直接应用预处理函数 if isinstance(item, str): return _remove_html_tag(item) # 处理Pydantic模型:递归处理每个字段 elif isinstance(item, BaseModel): for field_name in item.model_fields: field_value = getattr(item, field_name) processed_value = preprocess_item(field_value) setattr(item, field_name, processed_value) return item # 处理可迭代对象(排除字符串、字节串,避免重复处理) elif isinstance(item, Iterable) and not isinstance(item, (str, bytes)): # 处理可变容器(如列表):直接修改内部元素 if isinstance(item, list): for i in range(len(item)): item[i] = preprocess_item(item[i]) return item # 处理不可变容器(如元组):返回新的处理后的容器 elif isinstance(item, tuple): return tuple(preprocess_item(elem) for elem in item) # 其他可迭代类型(如集合)可按需扩展 else: return item # 非字符串、非模型、非可迭代的类型(如int、float)直接返回 else: return item
关键修改说明
- 返回值处理:每个分支都返回处理后的对象,确保递归调用能传递处理结果,顶层调用可直接获取修改后的模型
- 模型字段遍历:使用
model_fields获取模型定义的所有字段,避免遍历到模型的私有属性 - 类型泛化:将参数类型改为
Any,支持处理字符串、列表、元组等多种嵌套类型 - 可迭代对象区分:针对可变(列表)和不可变(元组)容器分别处理,保证修改生效的同时兼顾不可变类型的特性
- 字符串单独处理:直接对字符串应用预处理逻辑,无需递归调用
使用示例
# 定义你的模型 class OtherModel(BaseModel): other_id: int some_name: str class DummyModel(BaseModel): location_id: int other_models: list[OtherModel] name: str surname: str one_other_model: OtherModel # 创建测试实例 test_data = DummyModel( location_id=1, other_models=[ OtherModel(other_id=2, some_name="<b>Apple</b>"), OtherModel(other_id=3, some_name="<i>Banana</i>") ], name="<div>Alice</div>", surname="<span>Smith</span>", one_other_model=OtherModel(other_id=4, some_name="<u>Cherry</u>") ) # 执行预处理 processed_data = preprocess_item(test_data) # 验证结果 print(processed_data.name) # 输出: Alice print(processed_data.one_other_model.some_name) # 输出: Cherry print(processed_data.other_models[1].some_name) # 输出: Banana
内容的提问来源于stack exchange,提问作者mehekek
相关产品推荐
相关产品推荐

