Django migrate --fake和--fake-initial的解释

115

我已经使用Django约2年了,有一个特性一直让我害怕: 伪造迁移(faking migrations)

我已经搜索过很多地方,得到的大部分信息来源于文档,其中提到:

--fake

告诉Django标记迁移已经应用或未应用,但是不实际运行SQL更改数据库模式。

这是供高级用户直接操作当前迁移状态的选项,如果他们手动应用更改;请注意,使用--fake会冒着把迁移状态表置于需要手动恢复才能正确运行迁移的状态的风险。

--fake-initial

如果该迁移中的所有CreateModel操作创建的所有模型的名称与所有数据库表的名称相匹配,则允许Django跳过应用程序的初始迁移。此选项适用于首次针对在使用迁移之前就存在的数据库运行迁移。但是,此选项不会检查匹配的数据库模式除了匹配表名之外,因此只有在您确信现有模式与初始迁移中记录的模式相匹配时,才可以安全使用此选项。

我了解一般想法和为什么要使用这个功能,但是我不理解它说这是仅供高级用户使用。

有人能解释一下幕后发生的事情以及为什么需要手动恢复吗?

注意

我不是在寻找伪造迁移时运行的确切原始SQL查询。我只需要大体了解幕后发生的事情以及伪造迁移会导致makemigrations无法正常工作的示例。


1
我认为值得一提的是,当你运行--fake命令时,标记迁移是否已应用是在django_migrations表中定义的。Django在该表中跟踪应用于应用程序的所有迁移,包括迁移文件的名称和应用时间。我花了一些时间才弄清楚这一点,因为文档对此细节并没有明确说明。 - ivanleoncz
1个回答

104

这与数据库问题有关,类似于在源代码(git)中合并冲突的情况,如果您需要组合两个具有相似模型或在它们之间切换的分支。没有人会故意这样做。

想象一下,您上周开始修改一个应用程序,可能是因为您发现了一个错误或通过添加字段或表来扩展应用程序。今天您收到了更新,但出现了一个问题,因为有一个迁移(migration)正在添加一个字段,而该字段仍然存在于您的数据库中,因此您只能应用该迁移的其他部分。您可以运行以下命令查看迁移的 SQL 内容:

./manage sqlmigrate some_app 0007_new_migration >customized-some_app-0007_new_migration.sql

比较内容与上周所做的更改,并删除或注释掉仍然应用且无法重复的命令。手动运行所有剩余的SQL语句。将此迁移标记为自动应用:

./manage migrate --fake some_app 0007_new_migration

如果你不小心把某些东西破坏了,可能没有人能够帮你,因为迁移系统将不再知道数据库的当前状态。因此,请备份、写下笔记、使用沙盒并且认真工作。

编辑:迁移表django_migrations是所有应用程序中应用的迁移的简单列表。该表中的行应始终与数据库结构同步。可以通过正常的migrate应用迁移(或通过反向迁移应用到较旧的状态来取消应用迁移,但通常会有一些数据丢失)。虚假迁移仅将更改应用于django_migrations表。

me => select * from django_migrations;
 id | app      |          name           |            applied            
----+----------+-------------------------+-------------------------------
  1 | some_app | 0001_initial            | 2017-10-16 06:11:07.31249+02
  2 | some_app | 0002_auto_20171016_1905 | 2017-10-17 02:05:48.979295+02

迁移(文件)是增量变更的描述,也包括了信息,使得可以评估自上一次迁移以来models.py的差异,在运行makemigrations时进行比较。即使最初某些表格未经管理,并且它们稍后可能变为已管理状态,仍然足够记录这些未经管理的表格。

EDIT: 例如:如何使用--fakesqlmigrate 来通过迁移(重新创建删除的表)修复损坏的数据库

EDIT: 示例:如果您决定删除某个应用程序的表格并通过migrate来重新创建它们(请注意并查看下面的评论),则可能首先要重置该应用程序的所有迁移,包括初始迁移,使用伪迁移名称“zero”。
./manage migrate --fake some_app zero


谢谢你的回答!它帮助我看到了一个伪造迁移的真实情况,但我仍然想知道在伪造迁移时幕后发生了什么。 - scharette
@hynekcer,我有一个问题关于你最后的编辑,当我的迁移历史与我的数据库同步时,为什么要“重置所有迁移”?最近我遇到了类似的问题,我在表格中的一行添加了唯一属性。当我运行迁移时,我收到错误消息:“ProgrammingError:relation '...unique'已经存在”,但是当我删除了所有的迁移并重新运行此步骤时,它通过了?我必须指出,我的两个项目都在同一个数据库上运行,并且我正在两个应用程序中添加唯一属性。 - Unknown123
@Unknown123 我应该在最后一个示例中添加一些警告,比如如果它是在开发阶段且没有重要数据,并且同步已经非常破碎,无法进行前向或后向迁移,则模拟迁移到“零”并删除一些表可能比缓慢编辑 sqlmigrate 的结果更好,也比快速删除整个数据库更好。 - hynekcer
如果您的两个项目使用同一个数据库,则使用相同的models.py和相同的迁移目录非常重要。在一个项目中迁移的内容也将在第二个项目中进行迁移。如果无法使用公共模型,则可能会很糟糕,您将无法在第二个数据库中运行迁移,并且只能手动运行sqlmigrate的一小部分并设置Meta:managed=False。 - hynekcer
2
实际上,有很多情况需要伪造迁移。特别是当您想在另一个服务器中使用来自一个服务器的数据库转储时。当您遇到难以调试且除了生产服务器外无法重现的错误时,伪造非常有用。由于git冲突而搞乱迁移文件是由于配置不正确的.gitignore文件。这不是伪造迁移的用例的好示例。 - Mohammed Shareef C

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