7.4 最佳实践与下一步
项目跑通了,教程也接近尾声。本节做三件事:把散落全书的最佳实践汇总成清单、给出异步改造对照、规划你的下一步学习路线。
一、SQLAlchemy 最佳实践清单
架构与组织
- ✅ 一个数据库一个 Engine,模块级全局;Session 每个工作单元一个,用完即关
- ✅ 分层:routers 管 HTTP、crud 管数据库、models 管存储、schemas 管进出
- ✅ crud 函数不 commit(最多 flush),谁代表完整业务谁 commit
- ✅ 正式项目从第一天用 Alembic,别依赖
create_all
模型定义
- ✅
String永远写长度;金额用Decimal + Numeric;固定选项用enum.Enum - ✅ 每张表标配
created_at(server_default=func.now())与updated_at(+onupdate) - ✅ 外键列加
index=True;常被 where/排序的列加索引 - ✅ 每个模型写
__repr__ - ✅ 父子从属关系配
cascade="all, delete-orphan";多对多和弱关联不配
查询与性能
- ✅ 循环里访问关系属性前,必须
options(selectinload/joinedload)预加载 - ✅ 列表关系用 selectinload,单对象关系用 joinedload
- ✅ 分页必须配 order_by;接口返回 total
- ✅ 只需要两列就
select(User.name, User.email),别捞整个对象 - ✅ 开发期开
echo=True,瞄一眼 SQL 条数是否随数据量增长(N+1 探测)
安全与健壮性
- ✅ 原生 SQL 必须
text()+ 参数化,严禁 f-string 拼接(SQL 注入) - ✅ 唯一性检查两道防线:先查后插给友好提示 + 唯一约束兜底捕获
IntegrityError - ✅ commit 失败必须 rollback
- ✅ 密码只存哈希(bcrypt/argon2),出参 Schema 永远不含密码字段
- ✅ 批量 update/delete 前把 where 条件读三遍
- ✅ 生产配置(数据库密码等)放环境变量,不进代码库:
import os
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///app.db")
engine = create_engine(DATABASE_URL)
二、切换到 MySQL / PostgreSQL
项目要上线,把 SQLite 换成真正的服务器数据库:
# 1. 装驱动
# pip install pymysql (MySQL)
# pip install psycopg2-binary (PostgreSQL)
# 2. 改连接串(去掉 SQLite 专属的 check_same_thread)
engine = create_engine(
"mysql+pymysql://user:pass@localhost:3306/library?charset=utf8mb4",
pool_size=5,
max_overflow=10,
pool_recycle=3600,
pool_pre_ping=True, # 2.1 节的连接池参数在这里派上用场
)
模型、crud、路由零改动。(注:SQLite 里宽松通过的类型在 MySQL 会严格检查,跑一遍测试即可发现。)
三、异步版改造对照(衔接 5.4 节)
把实战项目改成异步只动两个文件,模式高度机械:
# database.py 异步版
from sqlalchemy.ext.asyncio import (AsyncSession, async_sessionmaker,
create_async_engine)
engine = create_async_engine("sqlite+aiosqlite:///library.db")
SessionLocal = async_sessionmaker(bind=engine, expire_on_commit=False)
async def get_db():
async with SessionLocal() as session:
yield session
DbSession = Annotated[AsyncSession, Depends(get_db)]
# 路由/crud:函数加 async,数据库调用加 await
@router.get("/books/{book_id}", response_model=schemas.BookOut)
async def get_book(book_id: int, db: DbSession):
book = await crud.get_book(db, book_id) # crud 内部同样 await
if book is None:
raise HTTPException(404, "图书不存在")
return book
注意事项回顾(5.4 节):expire_on_commit=False 必配;所有关系必须显式预加载(我们的 crud 层早就这么写了——这是分层 + 预加载习惯的红利)。
四、常见功能的下一步方向
实战项目还可以往这些方向扩展(每个都是真实工作中的高频需求):
| 功能 | 关键技术 | 提示 |
|---|---|---|
| 用户认证 | JWT + passlib(密码哈希)+ FastAPI 安全依赖 | 官方教程 Security 章节写得很好 |
| 权限控制 | 依赖注入链:get_current_user → require_admin | 依赖可以依赖另一个依赖 |
| 软删除 | is_deleted 标记(3.4 节) | 所有查询记得过滤 |
| 全文搜索 | PostgreSQL tsvector / MeiliSearch | LIKE 在大数据量下太慢 |
| 缓存 | Redis 缓存热点查询 | 注意缓存与数据库的一致性 |
| 后台任务 | FastAPI BackgroundTasks / Celery | 发邮件、生成报表 |
| 部署 | Docker + gunicorn/uvicorn + Nginx | 记得 echo=False、Alembic upgrade |
五、学习资源
- SQLAlchemy 官方文档:https://docs.sqlalchemy.org/ —— 尤其推荐 "ORM Quick Start" 和 "Unified Tutorial"
- FastAPI 官方文档:https://fastapi.tiangolo.com/zh/ —— 有高质量中文版,SQL Databases 章节与本教程互为印证
- Alembic 文档:https://alembic.sqlalchemy.org/
- Pydantic 文档:https://docs.pydantic.dev/
遇到问题的排查顺序:附录A 常见错误 → echo 日志看 SQL → 官方文档 → 搜索报错信息。
六、全教程知识地图(毕业检查)
对照下表自查,能独立完成右列任务即为掌握:
| 章节 | 毕业标准 |
|---|---|
| 1-2 章 | 不看资料写出:engine + Base + 一个带各种字段类型的模型 + create_all |
| 3 章 | 独立实现任意实体的 CRUD 函数,含动态搜索和分页 |
| 4 章 | 为"订单-商品-用户"设计出正确的表关系,知道每处外键放哪、何时预加载 |
| 5 章 | 会写 join + group_by 统计;会给项目接入 Alembic 并完成一次加字段迁移 |
| 6 章 | 独立搭出 database/models/schemas/main 四件套并解释每个零件的作用 |
| 7 章 | 向别人讲清楚借书接口为什么必须用事务,并能写测试证明它 |
🎓 结语
从"什么是数据库"到分层架构的完整 API 项目,你已经走完了大多数 Python 后端工程师的必经之路。接下来最好的学习方式是:用这套技术栈做一个你自己想要的项目——记账本、博客、追番清单,什么都行。真实需求会逼你把知识用活。
回到目录 → README | 遇到报错 → 附录 A:常见错误与解决方案