使用所需的ForeignKey引用在Django(1.8)应用程序之间移动模型

Shr*_*yas 28 python database django-models schema-migration

这是这个问题的扩展:如何在两个Django应用程序之间移动模型(Django 1.7)

我需要将一堆模型从中移动old_appnew_app.最好的答案似乎是Ozan的,但是需要外键引用,事情有点棘手.@halfnibble在对Ozan的答案的评论中提出了一个解决方案,但我仍然遇到了精确的步骤顺序问题(例如,何时将模型复制到new_app,何时删除模型old_app,哪些迁移将在old_app.migrationsvs中进行.new_app.migrations等)

任何帮助深表感谢!

Nos*_*.io 70

在应用之间迁移模型.

简短的回答是,不要这样做!!

但这个答案很少适用于生活项目和生产数据库的现实世界.因此,我创建了一个示例GitHub repo来演示这个相当复杂的过程.

我正在使用MySQL.(不,那些不是我真正的凭据).

问题

我正在使用的示例是一个带有汽车应用程序的工厂项目,该应用程序最初具有Car模型和Tires模型.

factory
  |_ cars
    |_ Car
    |_ Tires
Run Code Online (Sandbox Code Playgroud)

Car模型与ForeignKey有关系Tires.(如同,通过汽车模型指定轮胎).

但是,我们很快意识到这Tires将是一个拥有自己的视图等的大型模型,因此我们希望它在自己的应用程序中.因此,所需的结构是:

factory
  |_ cars
    |_ Car
  |_ tires
    |_ Tires
Run Code Online (Sandbox Code Playgroud)

我们需要保持ForeignKey之间的关系Car,Tires因为太多依赖于保留数据.

解决方案

步骤1.设置设计错误的初始应用程序.

浏览步骤1的代码.

步骤2.创建管理界面并添加一堆包含ForeignKey关系的数据.

查看第2步.

步骤3.决定将Tires模型移动到自己的应用程序.精心剪切并粘贴代码到新轮胎应用程序中.确保更新Car模型以指向新tires.Tires模型.

然后./manage.py makemigrations在某个地方运行并备份数据库(以防这种情况严重失败).

最后,运行./manage.py migrate并查看厄运的错误消息,

django.db.utils.IntegrityError:(1217,'无法删除或更新父行:外键约束失败')

步骤3中查看目前为止的代码和迁移.

第4步.棘手的部分.自动生成的迁移无法看到您只是将模型复制到其他应用程序.所以,我们必须做一些事情来解决这个问题.

您可以跟随并查看步骤4中的注释的最终迁移.我测试了这个以验证它是否有效.

首先,我们将继续努力cars.您必须进行新的空迁移.实际上,此迁移需要在最近创建的迁移(无法执行的迁移)之前运行.因此,我重新编号了我创建的迁移并更改了依赖项以首先运行我的自定义迁移,然后是cars应用程序的上次自动生成的迁移.

您可以使用以下命令创建空迁移:

./manage.py makemigrations --empty cars
Run Code Online (Sandbox Code Playgroud)

步骤4.a. 进行自定义old_app迁移.

在第一次自定义迁移中,我只会执行"database_operations"迁移.Django为您提供了拆分"状态"和"数据库"操作的选项.您可以在此处查看代码,了解如何完成此操作.

我在第一步的目标是重命名数据库表,oldapp_modelnewapp_model不是搞乱Django的状态.您必须根据应用程序名称和型号名称弄清楚Django将您的数据库表命名为什么.

现在您已准备好修改初始tires迁移.

步骤4.b. 修改new_app初始迁移

操作很好,但我们只想修改"状态"而不是数据库.为什么?因为我们从cars应用程序中保留数据库表.此外,您需要确保先前进行的自定义迁移是此迁移的依赖项.查看轮胎迁移文件.

所以,现在我们已经更名cars.Tirestires.Tires数据库中,并改变了Django的状态识别tires.Tires表.

步骤4.c. 修改old_app上次自动生成的迁移.

到汽车,我们需要修改最后自动生成的迁移.它应该需要我们的第一次定制汽车迁移,以及最初的轮胎迁移(我们刚刚修改过).

这里我们应该离开AlterField操作,因为Car模型指向不同的模型(即使它具有相同的数据).但是,DeleteModel由于cars.Tires模型不再存在,我们需要删除有关的迁移线.它已完全转换为tires.Tires.查看此迁移.

步骤4.d. 清理old_app中的陈旧模型.

最后但同样重要的是,您需要在汽车应用中进行最终的自定义迁移.在这里,我们将仅执行"状态"操作以删除cars.Tires模型.它是仅状态的,因为cars.Tires已重命名数据库表.这最后的迁移清理剩余的Django的状态.

  • 非常感谢!像魅力一样工作;)只是一个小建议:当你说"[我更改了数字]时更加明确(https://github.com/halfnibble/factory/blob/d4db34aa98a7ed141026de4b3fad9910f64f3854/cars/migrations/0003_auto_20150603_0630.py#L10 )".也许"交换迁移文件名"或类似的东西...... (2认同)
  • @Teekin 我想不出任何技术原因。虽然为了减少可能的歧义,我认为最好做出管理命令。也许`manage.py migrate --refactor cars.Tires > Tires.Tires` 或类似的东西。如果我有时间,我会自己做,但我必须以工作为生。:) (2认同)
  • 通用外键仍然会损坏!您还需要添加迁移以重命名 `django_contenttypes` 中的 `app_label` 以反映更改。 (2认同)