Python tortoise-orm 的生产迁移经验

1次阅读

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

Python tortoise-orm 的生产迁移经验

迁移命令执行后表没变,alembictortoise 混用了?

不是所有迁移工具能混用。Tortoise ORM 自带 generate_schemaupgrade_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 columncannot 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%';,有结果就得先删视图,迁移完再重建
  • 字段类型变更(比如 CharFieldTextField)尽量分两步:先加新字段,用代码双写过渡,再删旧字段——避免单次迁移里同时 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_schemainit_models),不是实时查 DB。

实操建议:

立即学习Python免费学习笔记(深入)”;

  • 改模型后,删掉 migrations/__pycache__.tortoise 缓存目录(如果有),再跑 generate——否则它可能基于旧 schema 认为“这列早就该有了”
  • 字段加了 NULL=True 但 DB 里对应列是 NOT NULL?Tortoise 不会自动补 defaultusing 子句,得手动编辑迁移脚本,在 ADD COLUMN 后加 ALTER COLUMN ... SET DEFAULT ...
  • db_column="user_id" 定义字段,但模型里写的是 user: fields.ForeignKeyRelation["User"]?Tortoise 可能误判外键字段名,导致迁移脚本漏掉约束——此时应显式指定 related_nameon_delete,减少推断

事情说清了就结束。最麻烦的永远不是语法,而是 Tortoise 把“模型→DB”当单向翻译,却没暴露足够多的中间态供你校验——所以每一步都得自己查 schema、看生成 SQL、盯住 pg_stat_activity。

text=ZqhQzanResources