🧩 ORM 技术选型指南:SQLAlchemy vs SQLModel
面向 Python 开发者(尤其是 FastAPI / 数据 / 爬虫工程师)的现代数据库操作实践
🔍 一、什么是 ORM?
ORM(Object-Relational Mapping,对象关系映射)是一种编程技术,它将数据库中的 表(table) 映射为编程语言中的 类(class),把 行(row) 映射为 对象(instance),把 列(column) 映射为 属性(attribute)。
它允许开发者用面向对象的方式操作数据库,而无需手动拼接 SQL 语句。
🌰 举例说明
# 定义一个用户模型(类 ↔ 表)
class User:
id = 1
name = "Alice"
# 创建一个用户对象(对象 ↔ 数据库中的一行)
user = User(name="Alice")
# 保存到数据库(无需写 INSERT)
session.add(user)
session.commit()
✅ 优势:代码更直观、可维护性强、减少 SQL 注入风险
❌ 劣势:复杂查询可能不如原生 SQL 灵活
🧱 二、SQLAlchemy:Python ORM 的“老大哥”
SQLAlchemy 是 Python 中最强大、最成熟的 ORM 框架之一,诞生于 2006 年,广泛应用于 Flask、Django(可选)、Pyramid 等项目中。
✅ 核心特点
- 功能极其丰富,支持复杂查询、事务、连接池等
- 底层控制力强,适合企业级应用
- 社区庞大,文档完善
- 但语法相对繁琐,学习曲线较陡
🚀 三、SQLModel:为 FastAPI 而生的现代 ORM
由 FastAPI 作者 Tiangolo 推出的 SQLModel,是 SQLAlchemy 和 Pydantic 的“完美结合体”。
它基于:
- ✅ SQLAlchemy:提供强大的数据库操作能力
- ✅ Pydantic:提供数据验证、类型提示、API 序列化
✅ 核心优势
特性 | 说明 |
---|---|
✅单一模型,双重用途 | 一个类同时用于数据库操作和 API 输入/输出,无需 models.py + schemas.py |
✅强类型提示 | 完美支持 mypy 、IDE 自动补全、类型检查 |
✅零额外代码生成 OpenAPI 文档 | 可直接作为 FastAPI 的 response_model ,自动集成 Swagger UI |
✅简洁现代的语法 | 使用 Python 类型注解定义字段,代码更直观 |
🆚 四、SQLAlchemy vs SQLModel:对比一览
对比项 | SQLAlchemy | SQLModel |
---|---|---|
定义方式 | Column(Integer) |
id: int = Field(...) |
类型提示 | 弱(需注解辅助) | 强(原生 Pydantic 支持) |
FastAPI 响应支持 | ❌ 需转为 dict 或使用 from_attributes=True |
✅ 可直接作为 response_model |
是否需要额外 schema | ✅ 通常需要 schemas.py |
❌ 不需要,模型即 schema |
主键定义 | Column(Integer, primary_key=True) |
Field(primary_key=True) |
默认值 | default=... in Column |
Field(default=...) 或 default_factory=... |
学习成本 | 高(功能多,概念复杂) | 中(更直观,适合现代 Python 开发者) |
适用场景 | 复杂企业级应用、高定制化 ORM | 快速开发、API 服务、中小型项目、数据管道 |
🛠️ 五、最佳实践建议:以 SQLModel 为主,按需使用 SQLAlchemy
“用 SQLModel 写 90% 的代码,用 SQLAlchemy 写 10% 的高性能/复杂查询”
✅ 推荐策略
场景 | 推荐工具 |
---|---|
日常增删改查(CRUD) | ✅ SQLModel ORM |
FastAPI 请求/响应模型 | ✅ SQLModel(直接作为 response_model ) |
爬虫数据入库(小批量) | ✅ SQLModel session.add() |
大批量数据插入(10万+) | ⚠️session.bulk_insert_mappings() (原生 SQLAlchemy) |
复杂查询(子查询、窗口函数) | ⚠️select(...) , text("...") (原生 SQLAlchemy) |
聚合分析(GROUP BY, AVG) | ✅ 混合使用:select(func.avg(...)) |
💡 六、关键认知:SQLModel 与 SQLAlchemy 是“一体两面”
SQLModel 并不是替代 SQLAlchemy,而是它的“现代化封装”。
🔗 它们的关系是:
SQLModel = SQLAlchemy ORM + Pydantic + 更好的默认配置
因此:
- ✅ 您定义的
class User(SQLModel, table=True)
本质上就是一个 SQLAlchemy ORM 类 - ✅
Session
就是 SQLAlchemy 的Session
- ✅
engine
就是 SQLAlchemy 的Engine
- ✅ 您可以无缝混用两种风格,无需重新连接或重复定义模型
🧪 七、实战代码:混合使用 SQLModel 与 SQLAlchemy
# main.py
from sqlmodel import SQLModel, Field, create_engine, Session
from sqlalchemy import select, text, func
# 1. 定义模型(SQLModel 方式)
class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
email: str
age: int
# 2. 创建引擎和会话
engine = create_engine("sqlite:///example.db")
SQLModel.metadata.create_all(engine)
# 3. 使用同一个 Session,自由切换风格
with Session(engine) as session:
# ✅ 场景1:普通插入(SQLModel 风格)
user = User(name="Alice", email="a@b.com", age=25)
session.add(user)
session.commit()
# ✅ 场景2:大批量插入(SQLAlchemy 原生方法)
bulk_data = [
{"name": "Bob", "email": "b@b.com", "age": 30},
{"name": "Charlie", "email": "c@c.com", "age": 35},
]
session.bulk_insert_mappings(User, bulk_data)
session.commit()
# ✅ 场景3:复杂查询(SQLAlchemy select)
users = session.exec(
select(User).where(User.age > 20)
).all()
# ✅ 场景4:原生 SQL 查询
result = session.exec(
text("SELECT name, COUNT(*) as cnt FROM user GROUP BY name")
).all()
# ✅ 场景5:聚合查询
avg_age = session.exec(
select(func.avg(User.age))
).one()
print(f"平均年龄: {avg_age}")
✅ 所有操作基于同一个连接、同一个会话、同一个模型,无缝切换,零成本集成。
🎯 八、给数据/爬虫工程师的终极建议
如果您是:
- ✅ 数据开发工程师
- ✅ 爬虫工程师
- ✅ FastAPI 开发者
- ✅ 脚本/项目并重
强烈推荐将 SQLModel 作为首选数据库工具。
因为它能:
- 🚀 提升开发效率:减少重复模型定义
- 🧪 增强代码安全:类型提示 + 数据验证
- 📦 统一数据流:爬虫 → 存库 → API 暴露,一个模型贯穿始终
- ⚡ 保留高性能选项:需要时直接调用 SQLAlchemy 原生方法
📌 总结
工具 | 定位 | 推荐使用场景 |
---|---|---|
SQLAlchemy | 功能全面的“底层引擎” | 复杂 ORM 操作、企业级系统 |
SQLModel | 现代化的“开发加速器” | FastAPI、数据管道、爬虫、快速开发 |
🔑 最佳策略:
以 SQLModel 为主力,享受开发效率;
在性能瓶颈处,无缝切入 SQLAlchemy 原生 API。