在实际开发中,我们经常需要在不同的数据结构(如字典、SQLAlchemy 模型、Pydantic 模型、Dataclass 等)之间进行转换。为了减少重复代码并提高转换的灵活性,设计一套通用的转换工具让代码更简洁。本文将通过两个核心函数实现数据对象与模型的双向转换。
一、为什么需要数据结构间的互转?
-
统一数据操作:不同的场景中使用的结构可能不同,但处理逻辑需要一致。
-
API
数据通常以字典形式传递。 -
数据库查询返回的是
SQLAlchemy
模型。 -
Pydantic
和Dataclass
是开发中常用的强类型模型。 -
减少手动转换:手动编写转换逻辑容易出错且效率低。
二、核心工具概览
本文将讲解两个核心函数:
-
data_to_model
:将各种数据对象(字典、SQLAlchemy、Pydantic、Dataclass 等)转换为目标模型。 -
model_to_data
:将各种模型对象(SQLAlchemy、Pydantic、Dataclass 等)还原为原始数据(字典或列表)。
三、从数据对象到模型:data_to_model
函数功能
data_to_model
可以将输入数据(字典、模型对象等)转换为目标模型(Pydantic、SQLAlchemy 或 Dataclass)。支持单个对象和列表对象的递归转换。
核心代码
python
代码解读
复制代码
@classmethod def data_to_model( cls, data_obj: Union[ dict, BaseOrmTable, BaseModel, dataclass, List[dict], List[BaseOrmTable], List[BaseModel], ], to_model: Type[Union[BaseModel, BaseOrmTable, dataclass]], ) -> Union[BaseModel, List[BaseModel], List[BaseOrmTable], List[dataclass], None]: """ 将数据对象转换成 pydantic 或 sqlalchemy 模型对象, 如果是数据库库表模型对象则调用to_dict()后递归 Args: data_obj: 支持 字典对象, pydantic、sqlalchemy模型对象, 列表对象 to_model: 转换后数据模型 Notes: - 对于实现了 to_dict() 方法的模型对象,将调用该方法返回字典。 returns: 转换后的对象 """ if isinstance(data_obj, dict): # 字典处理 return to_model(**data_obj) elif isinstance(data_obj, BaseOrmTable): # 数据库表模型对象处理, to_dict()后递归调用 return cls.data_to_model(data_obj.to_dict(), to_model=to_model) elif isinstance(data_obj, BaseModel): # pydantic v2 模型对象处理, model_dump 后递归调用 return cls.data_to_model(data_obj.model_dump(), to_model=to_model) elif dataclasses.is_dataclass(data_obj): # dataclass 模型对象处理, asdict() 后递归调用 return cls.data_to_model(asdict(data_obj), to_model=to_model) elif hasattr(data_obj, "to_dict"): # 如果模型对象有 to_dict 方法,调用该方法返回字典 return cls.data_to_model(data_obj.to_dict(), to_model=to_model) elif isinstance(data_obj, list): # 列表处理 return [cls.data_to_model(item, to_model=to_model) for item in data_obj] else: raise ValueError(f"不支持此{data_obj}类型的序列化转换")
转换流程
-
字典处理:直接解包为目标模型。
-
SQLAlchemy 模型处理:调用
to_dict
方法后递归处理。 -
Pydantic 模型处理:使用
model_dump
方法生成字典后递归处理。 -
Dataclass 处理:通过
asdict
方法生成字典后递归处理。 -
列表处理:逐个递归处理列表中的元素。
-
其他实现了
to_dict
方法的调用其to_dict
递归处理
使用示例
先定义一些 pydantic、dataclass 等模型对象
python
代码解读
复制代码
from dataclasses import dataclass from pydantic import BaseModel from sqlalchemy import Column, String from py_tools.connections.db.mysql import BaseOrmTable from py_tools.utils import SerializerUtil # sqlalchemy 示例 class UserTable(BaseOrmTable): __tablename__ = "user" username = Column(String(20)) email = Column(String(50)) # Pydantic 示例 class UserModel(BaseModel): id: int username: str email: str @dataclass class UserDataclass: id: int username: str email: str class UserCustomModel: def __init__(self, id: int, username: str, email: str): self.id = id self.username = username self.email = email def to_dict(self): return {"id": self.id, "username": self.username, "email": self.email}
数据转换demo
python
代码解读
复制代码
def data_to_model_demo(): user_table_obj = UserTable(id=2, username="wang", email="wang@example.com") user_model_obj = UserModel(id=3, username="zack", email="zack@example.com") user_dataclass_obj = UserDataclass(id=4, username="lisa", email="lisa@example.com") user_custom_model = UserCustomModel(id=5, username="lily", email="lily@example.com") user_infos = [ {"id": 1, "username": "hui", "email": "hui@example.com"}, user_table_obj, user_model_obj, user_dataclass_obj, user_custom_model, ] print("data_to_model") user_models = SerializerUtil.data_to_model(data_obj=user_infos, to_model=UserModel) print(type(user_models), user_models) user_models = SerializerUtil.data_to_model(data_obj=user_infos, to_model=UserTable) print(type(user_models), user_models) user_models = SerializerUtil.data_to_model(data_obj=user_infos, to_model=UserDataclass) print(type(user_models), user_models) user_models = SerializerUtil.data_to_model(data_obj=user_infos, to_model=UserCustomModel) print(type(user_models), user_models) user_model = SerializerUtil.data_to_model(data_obj=user_infos[0], to_model=UserModel) user_table = SerializerUtil.data_to_model(data_obj=user_infos[0], to_model=UserTable) user_dataclass = SerializerUtil.data_to_model(data_obj=user_infos[0], to_model=UserDataclass) print(type(user_model), user_model) print(type(user_table), user_table) print(type(user_dataclass), user_dataclass)
转换效果如下
四、从模型到数据对象:model_to_data
函数功能
model_to_data
将模型对象(SQLAlchemy、Pydantic 或 Dataclass)转换为字典或列表,适用于需要原始数据结构的场景。
核心代码
python
代码解读
复制代码
@classmethod def model_to_data( cls, model_obj: Union[ BaseModel, BaseOrmTable, dataclass, List[BaseModel], List[BaseOrmTable], List[dataclass], ], ) -> Union[dict, List[dict], None]: """ 将 Pydantic 模型或 SQLAlchemy 模型对象转换回原始字典或列表对象。 Args: model_obj: 支持 Pydantic 模型对象、SQLAlchemy 模型、dataclass 对象,或者它们的列表 Notes: - 对于实现了 to_dict() 方法的模型对象,将调用该方法返回字典。 Returns: 转换后的字典或列表 """ if isinstance(model_obj, dict): return model_obj if isinstance(model_obj, RowMapping): return dict(model_obj) elif isinstance(model_obj, BaseModel): # Pydantic 模型对象处理,model_dump() 返回字典 return model_obj.model_dump() elif isinstance(model_obj, BaseOrmTable): # SQLAlchemy 模型对象处理,to_dict() 返回字典 return model_obj.to_dict() elif dataclasses.is_dataclass(model_obj): # dataclass 模型对象处理, asdict() 返回字典 return asdict(model_obj) elif hasattr(model_obj, "to_dict"): # 如果模型对象有 to_dict 方法,调用该方法返回字典 return model_obj.to_dict() elif isinstance(model_obj, list): # 列表处理,递归转换每个元素 return [cls.model_to_data(item) for item in model_obj] else: raise ValueError(f"不支持此{model_obj}类型的反序列化转换")
转换流程
-
Pydantic 模型:使用
model_dump
方法生成字典。 -
SQLAlchemy 模型:通过
to_dict
方法生成字典。 -
Dataclass:通过
asdict
方法生成字典。 -
列表处理:递归处理每个元素。
-
其他实现了
to_dict
方法的调用其to_dict
生成字典
使用示例
python
代码解读
复制代码
def model_to_data_demo(): user_table_obj = UserTable(id=2, username="wang", email="wang@example.com") user_model_obj = UserModel(id=3, username="zack", email="zack@example.com") user_dataclass_obj = UserDataclass(id=4, username="lisa", email="lisa@example.com") user_custom_model = UserCustomModel(id=5, username="lily", email="lily@example.com") user_infos = [ {"id": 1, "username": "hui", "email": "hui@example.com"}, user_table_obj, user_model_obj, user_dataclass_obj, user_custom_model, ] # model_to_data print("model_to_data") user_infos = SerializerUtil.model_to_data(user_infos) print(type(user_infos), user_infos) user_info = SerializerUtil.model_to_data(user_model_obj) print(type(user_info), user_info) user_info = SerializerUtil.model_to_data(user_table_obj) print(type(user_info), user_info) user_info = SerializerUtil.model_to_data(user_dataclass_obj) print(type(user_info), user_info) user_info = SerializerUtil.model_to_data(user_custom_model) print(type(user_info), user_info)
转换效果如下:
五、总结
-
data_to_model
和model_to_data
提供了统一的数据转换能力,能够在字典、SQLAlchemy 模型、Pydantic 模型和 Dataclass 之间灵活转换。 -
在 Web 开发中,这两个函数可以用于 API 数据解析、数据库模型转换、响应数据格式化等多个场景。
通过这些工具,开发者可以显著提高代码的复用性和可维护性,同时让代码更清晰、简洁