当您应用迁移时,Django 会在名为 django_migrations 的表中插入一行。这是 Django 知道哪些迁移已经应用,哪些没有应用的唯一方法。因此,该表中的行必须与您的 migrations 目录中的文件匹配。如果您在应用迁移文件后丢失了迁移文件,或者做了其他任何事情以使事情不同步,那么您将遇到问题..因为数据库中的迁移编号与项目中的迁移文件引用的迁移文件不同。
因此,在您执行任何其他操作之前,您需要通过删除 django_migrations 表中的行来使您以某种方式丢失且无法恢复的任何迁移文件恢复同步。 该表应该只包含那些您确实拥有并且实际正确应用到数据库的迁移的行。
现在您需要处理 Django Migrations 不知道的数据库中的任何更改......为此有几个选项:
如果事情已经解决,已经应用到数据库的数据库更改与未应用的迁移文件位于不同的迁移文件中,那么您可以使用 --fake 一次运行一个迁移来修复它选择实际上已经在数据库中的任何更改。 fake 选项只是将行写入django_migrations 表,将迁移标记为已完成。仅当数据库实际上已经包含该迁移文件中包含的所有更改时才执行此操作。
那些只包含未应用到数据库的更改的迁移文件,在没有--fake 选项的情况下运行,Django 将应用它们。例如:
# database already has it
manage.py migrate myapp 0003 --fake
# need it
manage.py migrate myapp 0004
# database already has it
manage.py migrate myapp 0005 --fake
如果您的迁移文件中应用了部分但不是全部更改,那么您的问题就更大了。在这种情况下,有几种方法可以解决(仅选择一种):
-
编辑迁移文件以将已经应用的更改(无论是 Django 执行还是您手动执行都无关紧要)放入编号较小的迁移中,并将您需要完成的所有内容放入编号较大的文件中。现在您可以--fake 较低编号的,并正常运行较高编号的。假设您对模型进行了 10 次更改,其中 5 次实际上已经在数据库中,但是 Django 不知道它们。所以当您运行 makemigrations 时,将创建一个包含所有 10 项的新迁移变化。这通常会失败,因为数据库服务器无法添加例如已经存在的列。将这些已应用的更改从您的新迁移文件中移出,移到之前的(已应用的)迁移文件中。然后 Django 将假定这些已与先前的迁移一起应用,并且不会尝试再次应用它们。然后您可以像往常一样migrate,新的更改将被应用。
如果您不想修改旧的迁移文件,更简洁的方法是首先运行 makemigrations --empty appname 创建一个空的迁移文件。然后运行makemigrations,它将创建另一个迁移,其中包含 Django 认为需要完成的所有更改。将该文件中已完成的迁移移动到您创建的空迁移中。然后--fake 那个。这将使 Django 对数据库外观的理解与现实保持同步,然后您可以像往常一样migrate,应用上次迁移文件中的更改。
删除您刚刚使用 makemigrations 创建的任何新迁移。现在,注释掉或放回模型中尚未应用于数据库的任何内容,让您的代码与数据库中的实际内容相匹配。现在您可以执行makemigrations 和migrate appname --fake,您将恢复同步。然后取消注释您的新代码并正常运行'makemigrations'然后migrate,更改将被应用。如果更改很小(例如,添加几个字段),有时这是最简单的。如果变化很大,那就不是......
您可以继续(小心地)自己更改数据库,使数据库保持最新。现在只需运行migrate --fake,如果您没有搞砸,那么一切都会好起来的。同样,这对于较小的更改很容易,对于复杂的更改则不那么容易。
您可以运行manage.py sqlmigrate > mychanges.sql。这会生成mychanges.sql,其中包含 Django 将对数据库执行的所有 SQL。现在编辑该文件以删除已应用的任何更改,留下需要做的事情。使用pgadmin 或psql 执行该SQL(我希望你使用的是postgresql)。现在所有更改都已完成.. 所以你可以运行manage.py migrate --fake,这将使 Django 与现实同步,你应该一切就绪。如果您的 SQL 技能足够,这可能是最直接的解决方案。
我应该添加两个警告:
首先,如果您应用稍后的迁移,例如 0003_foobar.py,然后事情没有解决,并且您决定尝试返回并应用 0002_bazbuz.py,那么 Django 将从您的数据库中取出内容。例如,您可能在 0003 中添加的列将连同其数据一起被删除。既然你说你不能丢失数据,那么返回时要非常小心。
其次,不要急于运行--fake 迁移。确保您要伪造的整个迁移实际上已经在数据库中。否则它会变得非常混乱。如果您确实后悔伪造迁移并且不想回滚,则可以通过从 django_migrations 表中删除该行来消除 django 对伪造迁移的了解。可以这样做..如果您了解自己在做什么。如果您知道迁移确实没有应用,那就没关系。