SQLAlchemy的create_all()只创建缺失表,不更新现有表结构;Alembic需正确配置metadata、数据库URL和模型导入才能自动生成迁移脚本,升级失败时应通过stamp或downgrade安全回退。
SQLAlchemy 中 Base.metadata.create_all() 为什么不再自动更新表结构
它从来就不更新,只建缺失的表——这是最常被误解的一点。很多人以为改了 model 类、再跑一遍 create_all() 就能加字段或改类型,结果发现数据库毫无变化,甚至应用报错说字段不存在。
根本原因是:这个函数只做“存在性检查”,不对比模型与 DB 的差异。它读取当前所有已定义的 Table 对象,对每个表执行 SELECT 1 FROM pg_tables(PostgreSQL)或类似查询,仅当表完全不存在时才建表。
- 新增模型类 → 表会创建;删掉模型类 → 已有表不会被删
- 给已有模型加个
Column→ 不生效,DB 里还是老结构 - 改字段类型(如
String(50)→String(200))→ 完全无反应 - 想改主键或外键约束?
create_all()压根不碰 DDL 变更
用 alembic revision --autogenerate 生成迁移脚本前必须确认三件事
Autogenerate 不是魔法,它靠比对 env.py 里配置的模型模块和目标数据库当前 schema 来推断变更。一旦环境没对齐,生成的脚本可能漏改、错删,甚至把生产表 DROP 掉。
- 确保
target_metadata = Base.metadata指向你实际在用的模型基类,不是某个测试副本 - 数据库连接 URL 必须指向你希望“参考”的库(通常是开发库),不能是空库或旧快照
- 所有模型必须被
import进env.py或其导入链中,否则 autogenerate 看不见它们
常见错误现象:alembic revision --autogenerate -m "add user email" 生成空脚本,或只写了 op.create_table() 却没写 op.add_column() —— 八成是模型没 import 或 metadata 指错了。
alembic upgrade head 执行失败后如何安全回退
迁移失败时,Alembic 默认不会自动回滚已执行的语句(比如它成功加了列,但后续改默认值失败了)。这时手动干预必须分清“数据库状态”和“Alembic 记录状态”。
- 先查当前版本:
alembic current,看卡在哪条 revision ID - 如果只是 SQL 报错(如字段名冲突),可手动修复 DB,再用
alembic stamp <pre class="brush:php;toolbar:false;" v_rev></pre>把 Alembic 的记录指针拨回去 - 如果已部分生效且无法手动还原(比如删了关键数据),就别硬退,直接写一个
downgrade()补丁,在里面做反向操作 - 永远不要直接删
alembic_version表里的记录,也不要手改 DB schema 后跳过迁移
性能影响:大表加索引或改字段类型时,upgrade 可能锁表数分钟。线上操作务必在低峰期,并提前在同规格备库验证耗时。
团队协作中 alembic revision 提交前必须检查的两个文件
迁移脚本不是写完就能合入的。多人并行开发时,最容易出现“两个人同时基于同一版 DB 生成了不同 revision,合并后 upgrade 顺序错乱”,导致本地跑不通、CI 失败。
- 检查
alembic/versions/<hash>_xxx.py</hash>里的depends_on字段是否指向最新head,而不是某个分支上的旧 ID - 核对
alembic/env.py中的include_object回调(如果用了)是否过滤掉了不该参与 autogenerate 的临时表或视图
兼容性注意:PostgreSQL 和 MySQL 对 DROP COLUMN 支持不同,Alembic 生成的语句可能在一种库上 OK,另一种报错。跨库部署前,得在目标环境中实测 upgrade 和 downgrade。
模型变了,DB 不会自己跟上;工具能帮,但前提是知道它在哪停、在哪拐、踩了坑往哪摸回去。











