如何还原最近一次迁移?

766

我已经进行了一个迁移,添加了一个新的表,现在想回滚并删除该迁移,而不需要创建新的迁移。

我应该如何操作呢?是否有一个命令可以撤销最后一次迁移,然后我只需简单地删除迁移文件即可?

14个回答

1310

您可以通过将迁移回滚到上一个版本来恢复。

例如,如果您的最后两次迁移是:

  • 0010_previous_migration
  • 0011_migration_to_revert

那么您需要执行以下操作:

./manage.py migrate my_app 0010_previous_migration 

你实际上不需要使用完整的迁移名称,只需要用数字即可,例如:

./manage.py migrate my_app 0010 

您可以删除迁移0011_migration_to_revert

如果您使用的是Django 1.8+,则可以使用以下命令显示所有迁移的名称:

./manage.py showmigrations my_app

要撤销应用程序的所有迁移,可以运行以下命令:

./manage.py migrate my_app zero

19
我在 Stack Overflow 上看到了很多关于这个问题的回答,但它们都已经过时了,不再起作用。加上一分赞,因为这个方法适用于 Django 1.8。 - AlanSE
1
用户通常也想删除迁移文件吗?我经常发现自己创建了一个迁移,然后重新考虑并重新创建它,以在提交干净的代码之前做出修改,所以我通常需要记住 'rm path/to/0011_migration_to_revert.py'。 - LisaD
2
@Ness 这只是“反迁移”(de-migrates)操作。如果你不想再进行这个迁移,还需要删除迁移文件。 - dietbacon
4
只要使用实际的迁移名称,而不是“'0010_previous_migration'”,我就不知道为什么会出现那种行为。 - Alasdair
5
在使用Django 2.1时,以下命令会对我有用:/manage.py migrate my_app 0010,只需输入0010,而不是完整的文件名。之后,需要手动删除本地文件夹 appname/migrations/0011+ 中的文件,并手动从 django_migrations 数据库表中删除 0011+ 相关的行。 - Emily
显示剩余8条评论

59

在还原之前不要删除迁移文件。我犯了这个错误,没有迁移文件,数据库就不知道该删除哪些内容。

python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}
如果您想还原所有迁移,请使用zero作为迁移的名称:

If you want to revert all migrations, use zero as the name of the migration:

python manage.py migrate app_name_here zero

删除迁移文件。一旦您的模型中有所需的迁移...

python manage.py makemigrations
python manage.py migrate

49

Alasdair的答案涵盖了基础知识

  • 通过./manage.py showmigrations标识您想要的迁移
  • 使用应用程序名称和迁移名称进行migrate

但需要指出的是,并非所有迁移都可以被反转。这是因为Django没有规则来执行反转。对于大多数通过./manage.py makemigrations自动生成的迁移,反转是可能的。然而,自定义脚本需要编写正向和反向脚本,如此处的示例所述:

https://docs.djangoproject.com/en/1.9/ref/migration-operations/

如何进行无操作反转

如果您有一个RunPython操作,那么也许您只想撤消迁移,而不编写一个逻辑严谨的反转脚本。以下快捷方式修改自文档中的示例(上面的链接),允许此操作,在反转后仍将数据库保留在应用此迁移后的状态。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create([
        Country(name="USA", code="us"),
        Country(name="France", code="fr"),
    ])

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, lambda apps, schema_editor: None),
    ]

这适用于Django 1.8、1.9。


更新:更好的写法是将上面片段中的 lambda apps, schema_editor: None 替换为 migrations.RunPython.noop。它们在功能上是完全相同的。(感谢评论区)


7
从Django 1.8开始,您应该使用RunPython.noop代替内联的lambda表达式或等效形式:https://docs.djangoproject.com/en/1.8/ref/migration-operations/#django.db.migrations.operations.RunPython.noop - SpoonMeiser
1
@SpoonMeiser 在这个例子的语法中,我认为看起来像是 migrations.RunPython(forwards_func, migrations.RunPython.noop)。需要检查一下功能。这应该会在某个时候作为答案或编辑添加到这个问题中。 - AlanSE

16

这是我的解决方案,因为上面的解决方案并没有真正涵盖使用RunPython时的用例。

您可以通过ORM访问表格。

from django.db.migrations.recorder import MigrationRecorder

>>> MigrationRecorder.Migration.objects.all()
>>> MigrationRecorder.Migration.objects.latest('id')
Out[5]: <Migration: Migration 0050_auto_20170603_1814 for model>
>>> MigrationRecorder.Migration.objects.latest('id').delete()
Out[4]: (1, {u'migrations.Migration': 1})

您可以查询表格并删除与您相关的条目,这样您就可以进行详细修改。使用 RynPython 迁移时,您还需要处理添加/更改/删除的数据。上面的示例仅显示了如何通过 Djang ORM 访问表格。


在使用ForeignKeys创建新模型时,如果有多个迁移,并且意识到一切都是错误的,需要向后重新启动2-3个迁移,使用新模型,但有时可能具有相同的模型名称或相同的关系名称...这种解决方案显然是最好的。我遇到了错误消息,例如django.db.utils.ProgrammingError: relation "<relation name>" already exists,因此我进行了migrate --fake,这是错误的,所以我尝试回退,然后我得到了psycopg2.ProgrammingError: relation "<other <relation name>" does not exist。谢谢。 - onekiloparsec

13

撤销迁移:

python manage.py migrate <APP_NAME> <MIGRATION_NUMBER_PREFIX>

MIGRATION_NUMBER_PREFIX 是您想要还原的迁移的数字前缀,例如使用 0001 前缀即可回到 0001_initial.py 迁移。随后,您可以删除该迁移。

您可以将 zero 作为应用程序所有迁移的还原号码。


12

我在1.9.1版本中执行以下操作(删除最后或最新创建的迁移):

  1. rm <appname>/migrations/<migration #>*

    示例: rm myapp/migrations/0011*

  2. 登录数据库并运行以下SQL(以本例中的postgres为例)

    delete from django_migrations where name like '0011%';

然后,我就能够创建新的迁移,并从刚刚删除的迁移号码开始(在这种情况下是11)。


2
+1 虽然这样做可以解决问题,但你需要将它作为最后的选择。同时你也要记得编辑/删除那些有问题的迁移所涉及到的列/表。 - nehem
好的观点 - 我在创建迁移时使用了这个,但还没有运行“./manage.py migrate”。 - MIkee
1
绝对是危险的,根本不应该这样做。你可以在本地机器上操作,但那只是一种hack而不是解决方案。 - sajid

10

首先,找到您的应用程序的先前迁移:

python manage.py showmigrations

例如:

bz
 [X] 0001_initial
 [ ] 0002_bazifff


如果你想回滚 0002_bazifff 迁移,
python manage.py migrate bz 0001_initial

如果你想要回滚 0001_initial 或者 全部(all)

python manage.py migrate bz zero

看起来无法仅回滚0001


10

另一件可以做的事情是手动删除创建的表格。

与此同时,您还需要删除该特定迁移文件。此外,您还需删除 django-migrations 表中与该特定迁移相关的 特定条目(可能是您案例中的最后一个迁移)。


在这种情况下要小心 - 您有责任验证数据库是否适当。 - Sławomir Lenart
4
我会非常小心地添加。例如,在Postgres中,您可能会破坏许多内容,如约束条件。 - joedborg
如果表格没有外键,这种方法是可行的,但不建议使用。 - Touhami

8

您可以通过迁移到上一个迁移来还原。

例如使用以下命令:

./manage.py migrate example_app one_left_to_the_last_migration

然后删除 last_migration 文件。


7

如果您在回滚迁移时遇到麻烦,并且不小心搞乱了它,您可以执行“fake”迁移。

./manage.py migrate <name> --ignore-ghost-migrations --merge --fake

对于django版本小于1.7的情况,这将在south_migrationhistory表中创建一个条目,您需要删除该条目。

现在您可以轻松还原迁移。

提示:我卡了很长时间,执行虚假迁移然后还原帮了我。


2
此答案适用于 Django < 1.7。 - hynekcer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,