必须统一使用tortoise orm迁移流程:改模型→tortoise-orm generate -a→tortoise-orm upgrade;混用alembic会导致迁移失效、表结构不更新。

迁移命令执行后表没变,alembic 和 tortoise 混用了?
不是所有迁移工具能混用。Tortoise ORM 自带 generate_schema 和 upgrade_models,但不兼容 alembic 的迁移历史追踪机制——它压根不读 alembic_version 表,也不写 migration 文件。一旦你用 alembic revision --autogenerate 生成过文件,再切回 tortoise,就容易出现“执行了 migrate 却没建表/改字段”的假象。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 生产环境必须统一用
tortoise自己的迁移流程:先改models.py,再跑tortoise-orm generate -a(生成迁移脚本),最后tortoise-orm upgrade - 已有
alembic历史?别硬切。要么导出当前 DB 结构重置为 Tortoise 初始状态,要么停用tortoise迁移、全程交给alembic管理(需手动补全模型与迁移的映射) -
tortoise-orm generate默认只对比TORTOISE_ORM配置里的apps,漏配会导致跳过某些模型——检查config.py中"models"路径是否拼错、模块是否被 import 过
upgrade 报错 duplicate column 或 cannot alter type of a column used by a view
这是 postgresql 最常踩的坑:Tortoise 的自动迁移对视图、函数依赖、约束名不敏感,生成的 SQL 可能直接 ALTER COLUMN TYPE,而 PG 在列被视图引用时会拒绝执行。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 升级前手动查一遍:
select * FROM pg_views WHERE definition LIKE '%your_column_name%';,有结果就得先删视图,迁移完再重建 - 字段类型变更(比如
CharField→TextField)尽量分两步:先加新字段,用代码双写过渡,再删旧字段——避免单次迁移里同时ADD COLUMN+DROP COLUMN导致事务卡住 - 如果迁移脚本里出现
ALTER table ... RENAME COLUMN,确认目标列名在 DB 里确实存在;Tortoise 有时会把字段名大小写搞错(尤其用了db_column参数时),导致误判为新增列
生产环境不敢直接 upgrade,怎么安全灰度?
没有内置的“dry-run”模式,tortoise-orm upgrade --dry-run 只打印 SQL,但不校验实际执行可行性(比如索引名冲突、外键环)。真要灰度,得靠人工拆解和 DB 层控制。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 每次
generate后,打开生成的migrations/xxx.py,重点看upgrade函数里的await connection.execute_script(...)内容——它可能是一整段 SQL 字符串,而不是原子化操作 - 把脚本里的 SQL 复制出来,在测试库上用
BEGIN; ... ; ROLLBACK;包一层执行,观察锁表现和执行时间 - 涉及数据迁移(比如
update_queryset)的步骤,务必单独抽成管理命令(python manage.py migrate_user_status),不在自动迁移里做复杂逻辑 - 加个
pre_upgrade钩子:在迁移前自动备份关键表(pg_dump -t users > users_pre_migrate.sql),别依赖 Tortoise 的快照机制——它不存数据
generate 不识别模型变更,或反复生成同一行 ADD COLUMN
根本原因通常是模型定义和数据库实际结构“感知不同频”:Tortoise 读的是当前 Python 模型,但比对依据是它自己缓存的 _meta.db_schema(来自上次 generate_schema 或 init_models),不是实时查 DB。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 改模型后,删掉
migrations/__pycache__和.tortoise缓存目录(如果有),再跑generate——否则它可能基于旧 schema 认为“这列早就该有了” - 字段加了
NULL=True但 DB 里对应列是NOT NULL?Tortoise 不会自动补default或using子句,得手动编辑迁移脚本,在ADD COLUMN后加ALTER COLUMN ... SET DEFAULT ... - 用
db_column="user_id"定义字段,但模型里写的是user: fields.ForeignKeyRelation["User"]?Tortoise 可能误判外键字段名,导致迁移脚本漏掉约束——此时应显式指定related_name和on_delete,减少推断
事情说清了就结束。最麻烦的永远不是语法,而是 Tortoise 把“模型→DB”当单向翻译,却没暴露足够多的中间态供你校验——所以每一步都得自己查 schema、看生成 SQL、盯住 pg_stat_activity。