如何还原上一次迁移?


446

我进行了迁移,添加了一个新表,并希望还原该表并删除该迁移,而不创建新迁移。

我该怎么做?是否有命令还原上一次迁移,然后我可以简单地删除迁移文件?

Answers:


794

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

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

  • 0010_previous_migration
  • 0011_migration_to_revert

然后,您将执行以下操作:

./manage.py migrate my_app 0010_previous_migration 

然后,您可以删除迁移0011_migration_to_revert

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

./manage.py showmigrations my_app

要撤消应用程序的所有迁移,可以运行:

./manage.py migrate my_app zero

7
我已经看到许多关于该问题的答案,这些答案已经过时了,根本不再起作用了。+1,因为它适用于Django 1.8。
AlanSE'3

2
如果应用程序只有一个迁移文件/初始迁移该怎么办。我需要撤消该初始迁移吗?
阿迪亚特·穆巴拉克

37
migrate命令使您./manage.py migrate my_app zero可以取消应用程序的所有迁移。
阿拉斯代尔

4
由于某些原因,。/ manage.py migration my_app 0010_previous_migration对我不起作用。因此,我尝试使用./manage.py migration my_app 0010并成功。Django 1.8无法采用整个迁移名称的任何原因是什么?
Varun Verma,

4
只要您使用的是您的实际迁移名称,而不是'0010_previous_migration',我不知道您为什么会看到这种行为。
阿拉斯代尔

36

Alasdair的答案涵盖了基础知识

  • 确定您要进行的迁移 ./manage.py showmigrations
  • migrate 使用应用名称和迁移名称

但是应该指出,并非所有迁移可以逆转。如果Django没有规则进行逆转,则会发生这种情况。对于您通过进行自动迁移的大多数更改,./manage.py makemigrations都可以撤消。但是,自定义脚本将需要同时编写正向和反向脚本,如此处的示例所述:

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

如何进行无操作反转

如果您有一项RunPython操作,那么也许您只是想在不编写逻辑上严格的反向脚本的情况下取消迁移。以下来自docs(上链接)的示例的快速介绍允许这样做,即使恢复后,数据库仍保持与应用迁移后相同的状态。

# -*- 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: Nonemigrations.RunPython.noop。这些在功能上都是相同的。(记入评论)


5
从Django 1.8开始,您应该使用RunPython.noop而不是内联lambda或等价的:docs.djangoproject.com/en/1.8/ref/migration-operations/…–
SpoonMeiser

@SpoonMeiser在示例语法中,我认为它看起来像migrations.RunPython(forwards_func, migrations.RunPython.noop)。需要进行功能检查。有时应该将其添加为对此的答案或编辑。
AlanSE

12

这是我的解决方案,因为当您使用时上述解决方案并没有真正涵盖用例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

10

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

除此之外,您还必须删除该特定的迁移文件。另外,您将必须删除与该特定迁移相关的django-migrations表中的特定条目(在您的情况下可能是最后一个)。


在这种情况下要小心-您有义务验证db是否足够。
斯瓦沃米尔Lenart

4
我会非常小心。您可能会在Postgres中破坏很多东西,例如约束。
约堡2015年

9

还原后才删除迁移文件。我犯了这个错误,没有迁移文件,数据库不知道要删除什么内容。

python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}

删除迁移文件。在模型中完成所需的迁移后...

python manage.py makemigrations
python manage.py migrate

8

我在1.9.1中做到了这一点(删除了上一次创建的迁移或最新创建的迁移):

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

    例: rm myapp/migrations/0011*

  2. 登录数据库并运行此SQL(在此示例中为postgres)

    delete from django_migrations where name like '0011%';

然后,我可以创建新的迁移,该迁移以我刚刚删除的迁移号(在本例中为11)开头。


1
+1尽管可以,但是您需要将这种方式保存为万不得已。另外,您还必须记住编辑/删除导致迁移问题的列/表。
nehem

好点-我在创建迁移时使用了此功能,但尚未运行“ ./manage.py migration”
MIkee

3

如果Alasdair的最高答案无济于事,则此答案适用于类似情况。(例如,如果在每次新迁移时很快又创建了不想要的迁移,或者该迁移处于无法还原的较大迁移中,或者手动删除了该表。)

...删除迁移,而不创建新的迁移?

TL; DR:您可以删除一些最近恢复的(混淆的)迁移,并在修复模型后进行新的迁移。您还可以使用其他方法将其配置为不通过migration命令创建表必须创建最后一个迁移,以使其与当前模型匹配


案例为什么有人不想创建一个表必须存在一个型号:

A)这样的表应该不存在于没有机器,没有条件的数据库中

  • 何时:它是仅为其他模型的模型继承而创建的基础模型。
  • 解决方案:设置class Meta: abstract = True

B)很少通过其他方式或以特殊方式手动创建表。

  • 解决方案:使用class Meta: managed = False
    仅在测试中创建迁移,但从未使用过迁移。迁移文件很重要,否则,从可重现的初始状态开始,数据库测试将无法运行。

C)该表仅在某些机器上使用(例如,正在开发中)。

  • 解决方案:仅在特殊情况下或使用conditional将模型移至添加到INSTALLED_APPS的新应用程序class Meta: managed = some_switch

D)项目使用多个数据库settings.DATABASES

  • 解决方案:使用方法编写数据库路由器allow_migrate以区分应在何处创建表的数据库以及应在何处创建表的数据库。

迁移是在Django 1.9+的所有情况A),B),C),D)中创建的(并且仅在Django 1.8的情况下是B,C,D的情况下),但仅在适当的情况下才应用到数据库,或者如果要求如此。从Django 1.8开始,迁移对于运行测试是必需的。甚至对于Django 1.9+中具有Managed = False的模型,迁移也可以记录完整的相关当前状态,以便可以在托管/非托管模型之间创建ForeignKey或稍后使模型成为Managed = True。(这个问题是在Django 1.8时编写的。此处的所有内容对于1.8到当前2.2之间的版本均有效。)

如果上一次迁移不容易还原 ./manage.py migrate --fake my_app 0010_previous_migration,则可以(在数据库备份之后)谨慎地进行假还原,手动删除表。

如有必要,从固定模型创建固定迁移并应用它而不更改数据库结构./manage.py migrate --fake my_app 0011_fixed_migration


3

如果在还原迁移时遇到麻烦,并且由于某种原因将其弄乱了,则可以执行fake迁移。

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

对于django版本<1.7,它将在south_migrationhistory表中创建条目,您需要删除该条目。

现在,您将能够轻松地还原迁移。

PS:我被困了很多时间,进行了虚假的迁移,然后恢复原状帮助了我。


1
此答案适用于Django <1.7。
hynekcer
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.