bigywater.tech
Django · 53

RunPython 数据迁移

数据迁移最容易踩的坑:为什么不能用模型上的方法?为什么不能 import 真实模型?怎么回填才不会慢?这篇用 10 万行 PostgreSQL 数据把这几个问题一次跑清楚。

历史模型
RunPython 里 apps.get_model 返回 __fake__.Person,没有自定义方法
1.55 s vs 1.55 s
一条 UPDATE 回填 10 万行 vs Python 循环 + bulk_update 回填 2 万行
不要 import 真实模型
全新库上从头 migrate 时,真实模型的字段比当时的表多,查询会失败

历史模型

实测 · 本机执行输出
Operations to perform:
  Apply all migrations: contenttypes, lab
Running migrations:
  Applying lab.0002_fill_full...  历史模型 Person 是否有自定义方法 full_name: False  类: __fake__.Person
  Python 循环 + bulk_update(20000 行): 1.55 s
  一条 UPDATE … SET display = first_name || " " || last_name(100000 行): 0.18 s
 OK

迁移执行时,Django 用「迁移状态」构造一份临时的模型类(__fake__ 模块)。它的字段与该迁移那一刻一致,但没有任何方法、管理器(除非迁移里声明 use_in_migrations)、信号连接。

回填的写法与耗时

上面演示里「Python 循环」是读出来、改字段、bulk_update;「一条 UPDATE」是 update(display=Concat(F(...), Value(' '), F(...)))。耗时数字来自迁移自己打印的计时(本机 PostgreSQL,包含事务)。

正向与反向

实测 · 本机执行输出
  已填充的行数: 100000
  migrate lab 0001(回退)→ ['Unapplying lab.0002_fill_full... OK']  回退后 display 非空的行数: 0

回退时执行你提供的反向函数;不提供会 IrreversibleError(第 54 篇)。

import 真实模型的后果

实测 · 本机执行输出
  在全新库上从头 migrate(真实模型已多了 later 字段,但 0002 时这一列还不存在):
    django.db.utils.ProgrammingError: column lab_person.later does not exist

这个 bug 在开发库上看不出来(表已经是最新的),只在新环境从头迁移时才炸。

分步演示:数据迁移

RunPython 数据迁移
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。PostgreSQL 16.15(本机临时实例,端口 9430),10 万行;耗时由迁移函数内 perf_counter 打印
    • 没有验证:atomic = False 与分批提交的回填、RunSQL 的对比、超大表(千万行以上)、迁移里使用 use_in_migrations 的自定义 Manager、并发写入期间回填的一致性
    京ICP备2026067502号-1京公网安备11010502063487号