# SQLModel ORM 技术选型方案 > **文档版本**:v1.0 > **创建日期**:2025-01-27 > **决策状态**:✅ 已采纳 --- ## 背景 Jointo项目后端采用 FastAPI 框架,需要选择合适的 ORM 工具来管理数据库操作。原计划使用 SQLAlchemy 2.0,但在技术评审中发现 SQLModel 更适合本项目。 --- ## 问题分析 ### 传统 SQLAlchemy 的痛点 1. **重复代码**:需要分别定义 ORM Model 和 Pydantic Schema 2. **类型安全不足**:需要手动维护类型提示 3. **开发效率低**:模型变更需要同步修改多处 4. **学习曲线**:新手需要理解 SQLAlchemy 和 Pydantic 两套体系 ### 示例对比 **SQLAlchemy 方式(需要定义两次)**: ```python # models/project.py from sqlalchemy import Column, String, DateTime from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class ProjectModel(Base): __tablename__ = "projects" id = Column(String, primary_key=True) name = Column(String, nullable=False) description = Column(String) created_at = Column(DateTime) # schemas/project.py from pydantic import BaseModel from typing import Optional from datetime import datetime class ProjectCreate(BaseModel): name: str description: Optional[str] class ProjectResponse(BaseModel): id: str name: str description: Optional[str] created_at: datetime ``` **SQLModel 方式(只需定义一次)**: ```python # models/project.py from sqlmodel import SQLModel, Field from typing import Optional from datetime import datetime class ProjectBase(SQLModel): name: str = Field(max_length=255) description: Optional[str] = None class Project(ProjectBase, table=True): """数据库表模型""" id: Optional[str] = Field(default=None, primary_key=True) created_at: Optional[datetime] = Field(default_factory=datetime.utcnow) class ProjectCreate(ProjectBase): """创建请求模型""" pass class ProjectRead(ProjectBase): """读取响应模型""" id: str created_at: datetime ``` --- ## 方案对比 ### 方案 1:SQLModel(推荐)⭐ **技术特点**: - FastAPI 作者(Sebastián Ramírez)开发 - 基于 SQLAlchemy 2.0 + Pydantic v2 - 单一模型定义,同时用于 ORM 和 API Schema **优势**: - ✅ **类型安全**:完整的 TypeScript 级别类型提示 - ✅ **减少 50% 代码**:不需要重复定义 Model 和 Schema - ✅ **完美集成**:与 FastAPI 无缝配合 - ✅ **底层稳定**:继承 SQLAlchemy 2.0 的所有功能 - ✅ **异步支持**:原生 async/await - ✅ **学习曲线低**:统一的 API 设计 **劣势**: - ⚠️ 版本较新(0.0.14),但已在生产环境广泛使用 - ⚠️ 社区规模小于 SQLAlchemy(但增长迅速) **适用场景**: - ✅ 新项目(本项目) - ✅ 需要类型安全的项目 - ✅ FastAPI 项目 --- ### 方案 2:SQLAlchemy 2.0(保守方案) **技术特点**: - Python 最成熟的 ORM - 功能强大,支持复杂查询 **优势**: - ✅ 社区最大,文档最全 - ✅ 生产环境验证充分 - ✅ 支持所有高级特性 **劣势**: - ❌ 需要重复定义 Model 和 Schema - ❌ 类型安全需要手动维护 - ❌ 开发效率较低 **适用场景**: - 大型企业项目,稳定性优先 - 需要使用 SQLAlchemy 高级特性 --- ### 方案 3:Tortoise ORM **技术特点**: - 纯异步设计 - 类似 Django ORM **优势**: - ✅ 性能优秀 - ✅ 学习曲线低 **劣势**: - ❌ 仍需分别定义 Model 和 Schema - ❌ 社区规模小 - ❌ 功能不如 SQLAlchemy 强大 **适用场景**: - 简单 CRUD 应用 --- ## 决策结果 **选择方案 1:SQLModel** ### 决策理由 1. **与 FastAPI 完美集成**:同一作者开发,API 设计一致 2. **提升开发效率**:减少 50% 重复代码 3. **类型安全**:编译时类型检查,减少运行时错误 4. **底层稳定**:基于 SQLAlchemy 2.0,继承其稳定性 5. **支持复杂查询**:项目、分镜、资源等复杂关联查询 6. **异步支持**:与 FastAPI 的异步特性匹配 7. **迁移成本低**:完全兼容 Alembic 迁移工具 ### 风险评估 | 风险 | 等级 | 应对措施 | | -------- | ---- | ----------------------------------- | | 版本较新 | 低 | 底层是 SQLAlchemy 2.0,稳定性有保障 | | 社区规模 | 低 | FastAPI 作者维护,社区活跃度高 | | 功能缺失 | 低 | 可以直接使用 SQLAlchemy 的高级特性 | --- ## 实施计划 ### 1. 依赖安装 ```bash pip install sqlmodel==0.0.14 pip install alembic==1.12.0 ``` ### 2. 项目结构调整 ``` app/ ├── models/ # SQLModel 模型(同时用于 ORM 和 API) │ ├── project.py │ ├── storyboard.py │ └── ... ├── schemas/ # 额外的请求/响应模型(可选) │ └── common.py ``` ### 3. 数据库连接配置 ```python # app/core/database.py from sqlmodel import create_engine, Session engine = create_engine( settings.DATABASE_URL, echo=True, pool_pre_ping=True ) def get_session(): with Session(engine) as session: yield session ``` ### 4. 模型定义示例 ```python # app/models/project.py from sqlmodel import SQLModel, Field, Relationship from typing import Optional, List from datetime import datetime class Project(SQLModel, table=True): __tablename__ = "projects" id: Optional[str] = Field(default=None, primary_key=True) name: str = Field(index=True, max_length=255) description: Optional[str] = None type: str # 'mine' | 'collab' created_at: Optional[datetime] = Field(default_factory=datetime.utcnow) updated_at: Optional[datetime] = Field(default_factory=datetime.utcnow) # 关联关系 storyboards: List["Storyboard"] = Relationship(back_populates="project") ``` ### 5. API 使用示例 ```python # app/api/v1/projects.py from fastapi import APIRouter, Depends from sqlmodel import Session from app.models.project import Project from app.core.database import get_session router = APIRouter() @router.post("/projects", response_model=Project) async def create_project( project: Project, # 直接使用 SQLModel session: Session = Depends(get_session) ): session.add(project) await session.commit() await session.refresh(project) return project # 自动序列化 ``` --- ## 迁移路径 如果未来需要迁移回 SQLAlchemy: 1. SQLModel 底层就是 SQLAlchemy,迁移成本极低 2. 只需修改模型定义,查询 API 基本一致 3. 可以逐步迁移,不需要一次性重写 --- ## 参考资料 - [SQLModel 官方文档](https://sqlmodel.tiangolo.com/) - [FastAPI 官方文档](https://fastapi.tiangolo.com/) - [SQLAlchemy 2.0 文档](https://docs.sqlalchemy.org/) --- ## 变更记录 **v1.0 (2025-01-27)** - 初始版本 - 确定采用 SQLModel 作为 ORM 方案 - 完成技术对比和实施计划 --- **决策人**:后端架构团队 **审批状态**:✅ 已批准 **生效日期**:2025-01-27