So about a year ago I started a project and like all new developers I didn\'t really focus too much on the structure, however now I am further along with Django it has start
I get nervous hand-coding migrations (as is required by Ozan's answer) so the following combines Ozan's and Michael's strategies to minimize the amount of hand-coding required:
makemigrations
.app1
to app2
As recommended by @Michael, we point the new model to the old database table using the db_table
Meta option on the "new" model:
class Meta:
db_table = 'app1_yourmodel'
Run makemigrations
. This will generate CreateModel
in app2
and DeleteModel
in app1
. Technically, these migrations refer to the exact same table and would remove (including all data) and re-create the table.
In reality, we don't want (or need) to do anything to the table. We just need Django to believe that the change has been made. Per @Ozan's answer, the state_operations
flag in SeparateDatabaseAndState
does this. So we wrap all of the migrations
entries IN BOTH MIGRATIONS FILES with SeparateDatabaseAndState(state_operations=[...])
. For example,
operations = [
...
migrations.DeleteModel(
name='YourModel',
),
...
]
becomes
operations = [
migrations.SeparateDatabaseAndState(state_operations=[
...
migrations.DeleteModel(
name='YourModel',
),
...
])
]
You also need to make sure the new "virtual" CreateModel
migration depends on any migration that actually created or altered the original table. For example, if your new migrations are app2.migrations.0004_auto_
(for the Create
) and app1.migrations.0007_auto_
(for the Delete
), the simplest thing to do is:
app1.migrations.0007_auto_
and copy its app1
dependency (e.g. ('app1', '0006...'),
). This is the "immediately prior" migration in app1
and should include dependencies on all of the actual model building logic.app2.migrations.0004_auto_
and add the dependency you just copied to its dependencies
list.If you have ForeignKey
relationship(s) to the model you're moving, the above may not work. This happens because:
ForeignKey
changesForeignKey
changes in state_operations
so we need to ensure they are separate from the table operations.NOTE: Django 2.2 added a warning (models.E028
) that breaks this method. You may be able to work around it with managed=False
but I have not tested it.
The "minimum" set of operations differ depending on the situation, but the following procedure should work for most/all ForeignKey
migrations:
app1
to app2
, set db_table
, but DON'T change any FK references.makemigrations
and wrap all app2
migration in state_operations
(see above)
app2
CreateTable
to the latest app1
migrationmodels.py
(DON'T remove it) so it doesn't compete with the imported class.Run makemigrations
but DON'T wrap anything in state_operations
(the FK changes should actually happen). Add a dependency in all the ForeignKey
migrations (i.e. AlterField
) to the CreateTable
migration in app2
(you'll need this list for the next step so keep track of them). For example:
CreateModel
e.g. app2.migrations.0002_auto_
and copy the name of that migration.Find all migrations that have a ForeignKey to that model (e.g. by searching app2.YourModel
to find migrations like:
class Migration(migrations.Migration):
dependencies = [
('otherapp', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='relatedmodel',
name='fieldname',
field=models.ForeignKey(... to='app2.YourModel'),
),
]
Add the CreateModel
migration as as a dependency:
class Migration(migrations.Migration):
dependencies = [
('otherapp', '0001_initial'),
('app2', '0002_auto_'),
]
Remove the models from app1
makemigrations
and wrap the app1
migration in state_operations
.
ForeignKey
migrations (i.e. AlterField
) from the previous step (may include migrations in app1
and app2
).DeleteTable
already depended on the AlterField
migrations so I didn't need to manually enforce it (i.e. Alter
before Delete
).At this point, Django is good to go. The new model points to the old table and Django's migrations have convinced it that everything has been relocated appropriately. The big caveat (from @Michael's answer) is that a new ContentType
is created for the new model. If you link (e.g. by ForeignKey
) to content types, you'll need to create a migration to update the ContentType
table.
I wanted to cleanup after myself (Meta options and table names) so I used the following procedure (from @Michael):
db_table
Meta entrymakemigrations
again to generate the database renameDeleteTable
migration. It doesn't seem like it should be necessary as the Delete
should be purely logical, but I've run into errors (e.g. app1_yourmodel
doesn't exist) if I don't.