Migrating a model between apps.个
简短的答案是,don't do it!!
但这个答案在现实生活中的项目和生产数据库中很少奏效.因此,我创建了一个sample GitHub repo来演示这个相当复杂的过程.
我正在使用MySQL.(No, those aren't my real credentials).
The Problem个
我使用的示例是一个工厂项目,它有一个cars应用程序,最初有一个Car
型号和一个Tires
型号.
factory
|_ cars
|_ Car
|_ Tires
Car
型号与Tires
具有ForeignKey关系.(与中一样,您可以通过汽车模型指定轮胎).
然而,我们很快意识到,Tires
将是一个拥有自己的视图等的大型模型,因此我们希望它出现在自己的应用程序中.因此,所需的 struct 为:
factory
|_ cars
|_ Car
|_ tires
|_ Tires
我们需要将ForeignKey关系保持在Car
到Tires
之间,因为太多依赖于保存数据.
The Solution
Step 1.设置设计不佳的初始应用程序.
浏览step 1.的代码
Step 2.创建一个管理界面,并添加一组包含ForeignKey关系的数据.
视图step 2.
Step 3.决定将Tires
型号转移到自己的应用程序上.一丝不苟地将代码剪切并粘贴到新轮胎应用程序中.确保更新Car
模型以指向新的tires.Tires
模型.
然后运行./manage.py makemigrations
并在某个地方备份数据库(以防出现严重故障).
最后,运行./manage.py migrate
并查看Deom的错误消息,
django.db.utils.IntegrityError: (1217, 'Cannot delete or update a parent row: a foreign key constraint fails')个
查看到目前为止step 3.年的代码和迁移
Step 4.最棘手的部分.自动生成的迁移看不到您只是将一个模型复制到了不同的应用程序.因此,我们必须做一些事情来补救这一点.
您可以使用step 4.中的注释来跟踪并查看最终的migrations.我测试了一下,以验证它是否正常工作.
首先,我们将在cars
上下功夫.您必须进行一次新的空migrations.此迁移实际上需要在最近创建的迁移(执行失败的迁移)之前运行.因此,我对我创建的迁移进行了重新编号,并更改了依赖项,以便首先运行我的自定义迁移,然后为cars
个应用程序运行最后一次自动生成的migrations.
您可以使用以下命令创建空迁移:
./manage.py makemigrations --empty cars
Step 4.a.进行自定义old_appmigrations.
在这第一个自定义迁移中,我将只执行"DATABASE_OPERATIONS"migrations.Django为您提供了拆分"状态"和"数据库"操作的选项.您可以通过查看code here来了解这是如何实现的.
在第一步中,我的目标是在不影响Django的状态的情况下将数据库表重命名,从oldapp_model
重命名为newapp_model
.您必须根据应用程序名称和型号名称计算出Django会给您的数据库表命名什么.
现在,您已经准备好修改最初的tires
次migrations.
Step 4.b.修改new_app初始迁移
操作很好,但我们只想修改"状态",而不是数据库.为什么?因为我们保留了cars
应用程序中的数据库表.此外,您需要确保之前进行的自定义迁移是此迁移的依赖项.参见轮胎migration file.
因此,现在我们在数据库中将cars.Tires
重命名为tires.Tires
,并将Django状态更改为识别tires.Tires
表.
Step 4.c.修改old_app上次自动生成的migrations.
我们需要修改最后一次自动生成的migrations.它应该需要我们的第一次定制汽车迁移,以及最初的轮胎迁移(我们刚刚修改).
在这里,我们应该保留AlterField
操作,因为Car
模型is pointing是另一个模型(即使它有相同的数据).然而,我们需要删除DeleteModel
的迁移线,因为cars.Tires
模型已经不存在了.它已经完全变成了tires.Tires
.查看this migration.
Step 4.d.清理old_app中的陈旧模型.
最后但并非最不重要的一点是,您需要在Cars应用程序中进行最终的自定义migrations.在这里,我们将执行"状态"操作,仅删除cars.Tires
模型.它是仅状态的,因为cars.Tires
的数据库表已经被重命名.这last migration会清理剩下的Django 州.