This is an extension to this question: How to move a model between two Django apps (Django 1.7)
I need to move a bunch of models from old_app to new_app. The best answer seems to be Ozan's, but with required foreign key references, things are bit trickier. @halfnibble presents a solution in the comments to Ozan's answer, but I'm still having trouble with the precise order of steps (e.g. when do I copy the models over to new_app, when do I delete the models from old_app, which migrations will sit in old_app.migrations vs. new_app.migrations, etc.)
Any help is much appreciated!
Migrating a model between apps.
The short answer is, don't do it!!
But that answer rarely works in the real world of living projects and production databases. Therefore, I have created a sample GitHub repo to demonstrate this rather complicated process.
I am using MySQL. (No, those aren't my real credentials).
The Problem
The example I'm using is a factory project with a cars app that initially has a
Carmodel and aTiresmodel.The
Carmodel has a ForeignKey relationship withTires. (As in, you specify the tires via the car model).However, we soon realize that
Tiresis going to be a large model with its own views, etc., and therefore we want it in its own app. The desired structure is therefore:And we need to keep the ForeignKey relationship between
CarandTiresbecause too much depends on preserving the data.The Solution
Step 1. Setup initial app with bad design.
Browse through the code of step 1.
Step 2. Create an admin interface and add a bunch of data containing ForeignKey relationships.
View step 2.
Step 3. Decide to move the
Tiresmodel to its own app. Meticulously cut and paste code into the new tires app. Make sure you update theCarmodel to point to the newtires.Tiresmodel.Then run
./manage.py makemigrationsand backup the database somewhere (just in case this fails horribly).Finally, run
./manage.py migrateand see the error message of doom,django.db.utils.IntegrityError: (1217, 'Cannot delete or update a parent row: a foreign key constraint fails')
View code and migrations so far in step 3.
Step 4. The tricky part. The auto-generated migration fails to see that you've merely copied a model to a different app. So, we have to do some things to remedy this.
You can follow along and view the final migrations with comments in step 4. I did test this to verify it works.
First, we are going to work on
cars. You have to make a new, empty migration. This migration actually needs to run before the most recently created migration (the one that failed to execute). Therefore, I renumbered the migration I created and changed the dependencies to run my custom migration first and then the last auto-generated migration for thecarsapp.You can create an empty migration with:
Step 4.a. Make custom old_app migration.
In this first custom migration, I'm only going to perform a "database_operations" migration. Django gives you the option to split "state" and "database" operations. You can see how this is done by viewing the code here.
My goal in this first step is to rename the database tables from
oldapp_modeltonewapp_modelwithout messing with Django's state. You have to figure out what Django would have named your database table based on the app name and model name.Now you are ready to modify the initial
tiresmigration.Step 4.b. Modify new_app initial migration
The operations are fine, but we only want to modify the "state" and not the database. Why? Because we are keeping the database tables from the
carsapp. Also, you need to make sure that the previously made custom migration is a dependency of this migration. See the tires migration file.So, now we have renamed
cars.Tirestotires.Tiresin the database, and changed the Django state to recognize thetires.Tirestable.Step 4.c. Modify old_app last auto-generated migration.
Going back to cars, we need to modify that last auto-generated migration. It should require our first custom cars migration, and the initial tires migration (that we just modified).
Here we should leave the
AlterFieldoperations because theCarmodel is pointing to a different model (even though it has the same data). However, we need to remove the lines of migration concerningDeleteModelbecause thecars.Tiresmodel no longer exists. It has fully converted intotires.Tires. View this migration.Step 4.d. Clean up stale model in old_app.
Last but not least, you need to make a final custom migration in the cars app. Here, we will do a "state" operation only to delete the
cars.Tiresmodel. It is state-only because the database table forcars.Tireshas already been renamed. This last migration cleans up the remaining Django state.