在应用之间迁移模型。
简短的回答是,不要这样做!
但这个答案在现实世界的生活项目和生产数据库中很少奏效。因此,我创建了一个sample GitHub repo 来演示这个相当复杂的过程。
我正在使用 MySQL。 (不,这些不是我的真实凭据)。
问题
我使用的示例是一个带有 cars 应用程序的工厂项目,该应用程序最初具有 Car 模型和 Tires 模型。
factory
|_ cars
|_ Car
|_ Tires
Car 模型与 Tires 具有 ForeignKey 关系。 (如,您通过汽车模型指定轮胎)。
但是,我们很快意识到Tires 将成为一个拥有自己视图等的大型模型,因此我们希望它在自己的应用程序中。因此,所需的结构是:
factory
|_ cars
|_ Car
|_ tires
|_ Tires
我们需要保持Car 和Tires 之间的ForeignKey 关系,因为太多依赖于保存数据。
解决方案
第 1 步。设置设计不佳的初始应用。
浏览step 1.的代码
第 2 步。 创建一个管理界面并添加一堆包含 ForeignKey 关系的数据。
查看step 2.
第 3 步。 决定将 Tires 模型移至其自己的应用程序。小心翼翼地将代码剪切并粘贴到新的轮胎应用程序中。确保更新 Car 模型以指向新的 tires.Tires 模型。
然后运行./manage.py makemigrations 并在某处备份数据库(以防万一这严重失败)。
最后,运行./manage.py migrate,看看doom的错误信息,
django.db.utils.IntegrityError: (1217, '无法删除或更新父行:外键约束失败')
在step 3.中查看到目前为止的代码和迁移
第 4 步。 棘手的部分。自动生成的迁移看不到您只是将模型复制到不同的应用程序。所以,我们必须做一些事情来解决这个问题。
您可以在step 4. 中关注并查看使用 cmets 的最终迁移,我确实对此进行了测试以验证它是否有效。
首先,我们将处理cars。您必须进行新的空迁移。此迁移实际上需要在最近创建的迁移(执行失败的迁移)之前运行。因此,我重新编号了我创建的迁移并更改了依赖项以首先运行我的自定义迁移,然后是 cars 应用程序的最后一个自动生成的迁移。
您可以使用以下命令创建一个空迁移:
./manage.py makemigrations --empty cars
步骤 4.a. 进行自定义 old_app 迁移。
在第一次自定义迁移中,我将只执行“database_operations”迁移。 Django 为您提供了拆分“状态”和“数据库”操作的选项。您可以通过查看code here 来了解这是如何完成的。
我在第一步中的目标是将数据库表从 oldapp_model 重命名为 newapp_model 而不会弄乱 Django 的状态。你必须弄清楚 Django 会根据应用程序名称和模型名称来命名你的数据库表。
现在您可以修改初始 tires 迁移。
步骤 4.b. 修改 new_app 初始迁移
操作很好,但我们只想修改“状态”而不是数据库。为什么?因为我们保留了来自cars 应用程序的数据库表。此外,您需要确保先前进行的自定义迁移是此迁移的依赖项。看轮胎migration file。
所以,现在我们在数据库中将cars.Tires 重命名为tires.Tires,并更改了Django 状态以识别tires.Tires 表。
步骤 4.c. 修改 old_app 上次自动生成的迁移。
返回到汽车,我们需要修改最后一个自动生成的迁移。它应该需要我们的第一次定制汽车迁移,以及最初的轮胎迁移(我们刚刚修改)。
这里我们应该保留AlterField 操作,因为Car 模型指向不同的模型(即使它具有相同的数据)。但是,我们需要删除与DeleteModel 相关的迁移行,因为cars.Tires 模型不再存在。它已完全转换为tires.Tires。查看this migration。
步骤 4.d. 清理 old_app 中的陈旧模型。
最后但同样重要的是,您需要在汽车应用中进行最终的自定义迁移。在这里,我们将做一个“状态”操作,只删除cars.Tires 模型。它是仅状态的,因为 cars.Tires 的数据库表已被重命名。这个last migration 清理了剩余的 Django 状态。