如何手动运行Django迁移程序

213 阅读3分钟

通常情况下,你的Django项目的部署过程会运行migrate 命令,这样就可以在必要时更新你的数据库。特别是在较小的数据库中,Django的迁移系统可以为你 "直接做"。

但是,有时需要在你的数据库的SQL控制台中 "手工 "运行迁移。 我发现在较大的、繁忙的数据库中,以及在使用诸如 pt-online-schema-change在这篇文章中,我们将介绍手工运行迁移的过程,并对其进行调整,使之能够逆转迁移。

手动运行迁移程序

1.找到要运行的SQL

我们用Python编写Django迁移,但它们最终会运行一系列的SQL语句。 要手工运行一个迁移,你需要这些SQL语句,所以你可以自己运行它们。

你可以用Django的sqlmigrate 命令来显示一个迁移的SQL语句,比如。

$ ./manage.py sqlmigrate <app> <prefix>

这将输出每个迁移操作的注释标题,描述它的作用,然后是它的实际语句。 用迁移所在的应用程序的标签替换<app> 。用迁移名称的唯一前缀替换<prefix> --通常是一个四位数的数字,如0003

例如,要显示core 应用程序中名称以 "0003 "开头的迁移的SQL语句,你需要运行。

$ ./manage.py sqlmigrate core 0003
BEGIN;
--
-- Add field page_count to book
--
ALTER TABLE "core_book" ADD COLUMN "page_count" integer NULL;
COMMIT;

你只能在支持交易模式变化的数据库(Django内置后端中的SQLite和PostgreSQL)上看到与BEGINCOMMIT 的书名。必要时,可以在每次迁移中禁用这些功能。

如果任何操作不能以SQL方式运行,它们会有消息THIS OPERATION CANNOT BE WRITTEN AS SQL (从Django 4.1开始,旧版本的措辞略有不同)。这通常意味着使用RunPython 操作。对于这样的操作,将需要想办法在我这里所涉及的过程之外手工运行它们。 你也可以考虑将它们分割成自己的迁移。

一个小提示:最好针对你的生产数据库运行sqlmigrate 。对于某些操作,Django会查询数据库以找到对象的名称,特别是在迁移旧的索引定义时的索引名称。 根据你的各种环境的数据库是如何创建和迁移的,这些名称可能是不同的。 因此,Django在你的暂存服务器上生成的SQL可能与生产服务器上的不同。 但另一方面,使用你的生产设置与未执行的迁移并不总是容易的,所以你可能只想当心这个问题,必要时调整SQL。

2.逐条执行SQL语句

用Django的dbshell 命令在目标环境上打开你的数据库的SQL shell。

$ ./manage.py dbshell

在这里,你可以从sqlmigrate ,逐一运行迁移的SQL语句。跳过从sqlmigrate (开始的那几行)的注释,并确保将整条语句复制到 。 --),并确保你复制了以; 结尾的整个SQL语句。

例如,在PostgreSQL上运行上述程序。

$ ./manage.py dbshell
psql (14.4, server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.

example=# BEGIN;
BEGIN
example=*# ALTER TABLE "core_book" ADD COLUMN "page_count" integer NULL;
ALTER TABLE
example=*# COMMIT;
COMMIT
example=#

在运行迁移的同时,你应该用你使用的任何监控工具留意你的数据库的关键指标。你可能还想用第二个dbshell 来运行一些管理命令,例如在我最近的PostgreSQL文章中,我介绍了寻找和停止阻塞ALTER TABLE的查询。

对于支持事务性模式变化的数据库(SQLite、PostgreSQL),有一点需要注意。如果迁移中只有一条模式变化的语句,比如ALTER TABLE ,你可以放弃BEGINCOMMIT 。这意味着运行的SQL较少,你持有模式锁的时间也会稍短,减少迁移影响你繁忙的生产数据库的风险。

3.记录迁移的执行情况

在你手工运行了迁移语句之后,你需要记录迁移的执行情况。如果你不这样做,migrate 命令会尝试再次执行该迁移,这可能会导致灾难性的结局(但通常只会导致 "列已经存在 "这样的错误)。

Django的迁移系统在一个名为django_migrations 的表中记录了已执行的迁移。你可以用这个查询模板记录一个已执行的迁移。

$ ./manage.py migrate --fake <app> <prefix>

<app> 替换为该迁移所在应用的标签。将<name> 替换为该迁移的全称,即该迁移的文件名,不含扩展名.py 。(注意,不要只使用前缀,如0003!

例如,将我们的迁移示例标记为完成。

example=# INSERT INTO django_migrations (app, name, applied) VALUES ('core', '0003_book_page_count', NOW());
INSERT 0 1

你可以通过视觉上的比较来检查你的条目看起来是否正确。

SELECT * FROM django_migrations ORDER BY applied DESC;

就这样,你就完成了迁移的应用

"手工 "反转迁移

你可以使用上述过程来 "手工 "逆转迁移,只需做一些修改。

在第一步中,使用 --backwards标志到sqlmigrate ,生成SQL语句来撤销迁移。 使用这个模板。

$ ./manage.py sqlmigrate --backwards <app> <prefix>

在第三步中,你要从django_migrations 表中删除,而不是插入。 使用这个模板。

DELETE FROM django_migrations WHERE app = <app> AND name = <name>;

希望你不需要经常回滚迁移!