How to move a model between two Django apps (Django 1.7)
I encountered the same problem. Ozan's answer helped me a lot but unfortunately was not enough. Indeed I had several ForeignKey linking to the model I wanted to move. After some headache I found the solution so decided to post it to solve people time.
You need 2 more steps:
- Before doing anything, change all your
ForeignKey
linking toTheModel
intoIntegerfield
. Then runpython manage.py makemigrations
- After doing Ozan's steps, re-convert your foreign keys: put back
ForeignKey(TheModel)
instead ofIntegerField()
. Then make the migrations again (python manage.py makemigrations
). You can then migrate and it should work (python manage.py migrate
)
Hope it helps. Of course test it in local before trying in production to avoid bad suprises :)
I am removing the old answer as may result in data loss. As ozan mentioned, we can create 2 migrations one in each app. The comments below this post refer to my old answer.
First migration to remove model from 1st app.
$ python manage.py makemigrations old_app --empty
Edit migration file to include these operations.
class Migration(migrations.Migration):
database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]
state_operations = [migrations.DeleteModel('TheModel')]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=database_operations,
state_operations=state_operations)
]
Second migration which depends on first migration and create the new table in 2nd app. After moving model code to 2nd app
$ python manage.py makemigrations new_app
and edit migration file to something like this.
class Migration(migrations.Migration):
dependencies = [
('old_app', 'above_migration')
]
state_operations = [
migrations.CreateModel(
name='TheModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
],
options={
'db_table': 'newapp_themodel',
},
bases=(models.Model,),
)
]
operations = [
migrations.SeparateDatabaseAndState(state_operations=state_operations)
]
How I did it (tested on Django==1.8, with postgres, so probably also 1.7)
Situation
app1.YourModel
but you want it to go to: app2.YourModel
- Copy YourModel (the code) from app1 to app2.
add this to app2.YourModel:
Class Meta: db_table = 'app1_yourmodel'
$ python manage.py makemigrations app2
A new migration (e.g. 0009_auto_something.py) is made in app2 with a migrations.CreateModel() statement, move this statement to the initial migration of app2 (e.g. 0001_initial.py) (it will be just like it always have been there). And now remove the created migration = 0009_auto_something.py
Just as you act, like app2.YourModel always has been there, now remove the existence of app1.YourModel from your migrations. Meaning: comment out the CreateModel statements, and every adjustment or datamigration you used after that.
And of course, every reference to app1.YourModel has to be changed to app2.YourModel through your project. Also, don't forget that all possible foreign keys to app1.YourModel in migrations have to be changed to app2.YourModel
Now if you do $ python manage.py migrate, nothing has changed, also when you do $ python manage.py makemigrations, nothing new has been detected.
Now the finishing touch: remove the Class Meta from app2.YourModel and do $ python manage.py makemigrations app2 && python manage.py migrate app2 (if you look into this migration you'll see something like this:)
migrations.AlterModelTable( name='yourmodel', table=None, ),
table=None, means it will take the default table-name, which in this case will be app2_yourmodel.
- DONE, with data saved.
P.S during the migration it will see that that content_type app1.yourmodel has been removed and can be deleted. You can say yes to that but only if you don't use it. In case you heavily depend on it to have FKs to that content-type be intact, don't answer yes or no yet, but go into the db that time manually, and remove the contentype app2.yourmodel, and rename the contenttype app1.yourmodel to app2.yourmodel, and then continue by answering no.
This can be done fairly easily using migrations.SeparateDatabaseAndState
. Basically, we use a database operation to rename the table concurrently with two state operations to remove the model from one app's history and create it in another's.
Remove from old app
python manage.py makemigrations old_app --empty
In the migration:
class Migration(migrations.Migration):
dependencies = []
database_operations = [
migrations.AlterModelTable('TheModel', 'newapp_themodel')
]
state_operations = [
migrations.DeleteModel('TheModel')
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=database_operations,
state_operations=state_operations)
]
Add to new app
First, copy the model to the new app's model.py, then:
python manage.py makemigrations new_app
This will generate a migration with a naive CreateModel
operation as the sole operation. Wrap that in a SeparateDatabaseAndState
operation such that we don't try to recreate the table. Also include the prior migration as a dependency:
class Migration(migrations.Migration):
dependencies = [
('old_app', 'above_migration')
]
state_operations = [
migrations.CreateModel(
name='TheModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
],
options={
'db_table': 'newapp_themodel',
},
bases=(models.Model,),
)
]
operations = [
migrations.SeparateDatabaseAndState(state_operations=state_operations)
]