用 Alembic,别自己写迁移脚本——除非明确知道 web.py 的 db.query() 无法处理事务回滚、版本依赖和并发冲突;Alembic 是 SQLAlchemy 官方维护的生产级迁移工具,通过自动对比模型与数据库差异、生成带依赖的 upgrade/downgrade 脚本、记录版本到 alembic_version 表等方式,解决手写脚本难以应对的安全回退、并发冲突和顺序执行问题。

直接结论:用 Alembic,别自己写迁移脚本——除非你明确知道 web.py 的 db.query() 无法处理事务回滚、版本依赖和并发冲突。
Alembic 是什么,为什么它比手写 SQL 脚本更可靠
Alembic 是 SQLAlchemy 官方维护的迁移工具,不是“可选插件”,而是生产级数据库演进的事实标准。它解决的是手写脚本根本没法应对的问题:比如某次升级失败后如何安全回退、多人同时提交迁移时如何避免版本号冲突、表结构变更时如何保证数据不丢失。
常见错误现象:sqlalchemy.exc.ProgrammingError: column "email" does not exist —— 这通常不是 SQL 写错了,而是迁移没按顺序执行,或者本地版本落后于团队最新迁移文件。
- Alembic 通过
alembic revision --autogenerate自动对比模型与数据库差异,生成带依赖关系的迁移文件(含upgrade()和downgrade()) - 每个迁移文件有唯一哈希 ID,运行时记录在
alembic_version表中,杜绝“我本地跑过了,线上却报错”这类问题 - 支持离线模式:生成纯 SQL 脚本供 DBA 审核,不直接连库执行
web.py 项目里怎么接入 Alembic
web.py 没有内置 ORM,所以不能像 Django 那样靠 makemigrations 自动推导。但你可以用 SQLAlchemy 定义模型,再让 Alembic 管理这些模型对应的数据库变更——web.py 只负责查询,Alembic 负责建表/改表。
立即学习“Python免费学习笔记(深入)”;
关键步骤:
- 安装:
pip install alembic sqlalchemy - 初始化:
alembic init alembic,然后修改alembic.ini中的sqlalchemy.url指向你的 web.py 数据库连接串(如postgresql://user:pass@localhost/mydb) - 在
alembic/env.py里把你的 SQLAlchemymetadata对象赋给target_metadata,否则 autogenerate 会失效 - 每次改完模型,运行:
alembic revision --autogenerate -m "add user email field",再alembic upgrade head
注意:web.py 的 db 实例不能直接传给 Alembic;必须用独立的 SQLAlchemy Engine,否则事务隔离和连接池行为不可控。
什么时候该放弃 Alembic,改用手动脚本
不是所有场景都适合 Alembic。如果你的项目满足以下任意一条,手写迁移反而更清晰:
- 只做一次性迁移(比如从旧系统导入历史数据),且没有后续迭代需求
- 数据库权限受限,不允许创建
alembic_version表或执行 DDL - 需要在迁移过程中嵌入业务逻辑(例如:把旧表的
status字符串映射成新表的枚举值),而 Alembic 的op.bulk_insert()不够灵活
这时推荐用 web.py 原生方式写迁移函数,但务必加上版本标记和幂等检查:
def upgrade_v20260601(db):
# 先查表是否存在,避免重复执行
if not db.query("SELECT 1 FROM pg_tables WHERE tablename = 'orders'").list():
db.query("CREATE TABLE orders (...)")
别漏掉 if_exists='skip' 类逻辑——web.py 没有内置这个参数,得自己查元数据判断。
容易被忽略的兼容性坑
Python Web 项目里最容易栽跟头的地方,不是语法,而是环境一致性:
- Alembic 版本要和 SQLAlchemy 版本匹配:2.x 系列 Alembic 必须配 SQLAlchemy 2.x,混用会导致
AttributeError: 'Table' object has no attribute 'c' - PostgreSQL 的
serial和 SQLite 的INTEGER PRIMARY KEY在 autogenerate 时可能被误判为不一致,手动在模型里加server_default=func.nextval(...)才能对齐 - web.py 默认不开启连接自动提交,但 Alembic 的
upgrade命令会强制 commit;如果在同一个进程里混用 web.pydb和 Alembicconnection,可能触发psycopg2.OperationalError: server closed the connection unexpectedly
真正麻烦的从来不是“怎么写迁移”,而是“怎么让迁移在 CI、测试环境、生产环境跑出完全一致的结果”。版本锁死、连接隔离、SQL 审计日志——这些才是落地时卡住进度的关键点。


















