如何基于dataclasses构建兼容SQLAlchemy命令式映射的Pydantic自引用模型
解决方案:分层隔离领域模型与校验模型
你可以采用「纯标准库dataclass做领域层模型 + 单独声明Pydantic BaseModel做接口校验/序列化层」的方案,完全满足你的需求:既兼容SQLAlchemy命令式映射、支持自引用,也符合DDD依赖倒置的分层原则。
具体实现步骤
1. 定义纯领域模型
领域层完全不依赖任何第三方框架,仅用Python标准库dataclass实现,可直接用于SQLAlchemy命令式映射:
from __future__ import annotations from dataclasses import dataclass from typing import Optional @dataclass class Person: name: str age: int parent: Optional[Person] = None
2. 定义独立的Pydantic校验/序列化模型
单独定义和领域模型字段对齐的Pydantic BaseModel,用成熟的update_forward_refs方法解决自引用问题:
from pydantic import BaseModel from typing import Optional class PersonSchema(BaseModel): name: str age: int parent: Optional[PersonSchema] = None class Config: orm_mode = True # 开启后支持直接从dataclass/ORM实例转换为Pydantic实例 PersonSchema.update_forward_refs()
3. 双向转换使用
# 领域模型实例(可由SQLAlchemy查询直接返回) john = Person(name="John", age=49, parent=None) tim = Person(name="Tim", age=14, parent=john) # 领域模型转Pydantic实例,用于接口返回序列化 tim_schema = PersonSchema.from_orm(tim) print(tim_schema.dict()) # 输出:{'name': 'Tim', 'age': 14, 'parent': {'name': 'John', 'age': 49, 'parent': None}} # 接口入参校验后转领域模型实例,用于业务逻辑处理/持久化 input_data = {"name": "Lily", "age": 10, "parent": tim_schema.dict()} lily_schema = PersonSchema(**input_data) lily = Person(**lily_schema.dict())
备选方案(不推荐):手动更新Pydantic dataclass前向引用
如果你一定要直接使用Pydantic dataclass,可以手动调用其底层绑定的BaseModel的update_forward_refs方法解决问题,缺点是领域模型会绑定Pydantic依赖,不符合DDD分层原则:
from __future__ import annotations from pydantic.dataclasses import dataclass as pydantic_dataclass from typing import Optional @pydantic_dataclass class PydanticPerson: name: str age: int parent: Optional[PydanticPerson] = None # 手动更新底层Pydantic模型的前向引用 PydanticPerson.__pydantic_model__.update_forward_refs() # 测试正常运行 john = PydanticPerson(name="John", age=49, parent=None) tim = PydanticPerson(name="Tim", age=14, parent=john) print(tim)
内容的提问来源于stack exchange,提问作者Lionel Hamayon
相关产品推荐
相关产品推荐

