You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.0 KiB
7.0 KiB
SQLModel ORM 技术选型方案
文档版本:v1.0
创建日期:2025-01-27
决策状态:✅ 已采纳
背景
Jointo项目后端采用 FastAPI 框架,需要选择合适的 ORM 工具来管理数据库操作。原计划使用 SQLAlchemy 2.0,但在技术评审中发现 SQLModel 更适合本项目。
问题分析
传统 SQLAlchemy 的痛点
- 重复代码:需要分别定义 ORM Model 和 Pydantic Schema
- 类型安全不足:需要手动维护类型提示
- 开发效率低:模型变更需要同步修改多处
- 学习曲线:新手需要理解 SQLAlchemy 和 Pydantic 两套体系
示例对比
SQLAlchemy 方式(需要定义两次):
# 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 方式(只需定义一次):
# 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
决策理由
- 与 FastAPI 完美集成:同一作者开发,API 设计一致
- 提升开发效率:减少 50% 重复代码
- 类型安全:编译时类型检查,减少运行时错误
- 底层稳定:基于 SQLAlchemy 2.0,继承其稳定性
- 支持复杂查询:项目、分镜、资源等复杂关联查询
- 异步支持:与 FastAPI 的异步特性匹配
- 迁移成本低:完全兼容 Alembic 迁移工具
风险评估
| 风险 | 等级 | 应对措施 |
|---|---|---|
| 版本较新 | 低 | 底层是 SQLAlchemy 2.0,稳定性有保障 |
| 社区规模 | 低 | FastAPI 作者维护,社区活跃度高 |
| 功能缺失 | 低 | 可以直接使用 SQLAlchemy 的高级特性 |
实施计划
1. 依赖安装
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. 数据库连接配置
# 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. 模型定义示例
# 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 使用示例
# 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:
- SQLModel 底层就是 SQLAlchemy,迁移成本极低
- 只需修改模型定义,查询 API 基本一致
- 可以逐步迁移,不需要一次性重写
参考资料
变更记录
v1.0 (2025-01-27)
- 初始版本
- 确定采用 SQLModel 作为 ORM 方案
- 完成技术对比和实施计划
决策人:后端架构团队
审批状态:✅ 已批准
生效日期:2025-01-27