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 started to appear that my project layout mainly my models are horrible in structure.
I have models mainly held in a single app and really most of these models should be in their own individual apps, I did try and resolve this and move them with south however I found it tricky and really difficult due to foreign keys ect.
However due to Django 1.7 and built in support for migrations is there a better way to do this now?
This is tested roughly, so do not forget to backup your DB!!!
For example, there are two apps:
src_app
anddst_app
, we want to move modelMoveMe
fromsrc_app
todst_app
.Create empty migrations for both apps:
Let's assume, that new migrations are
XXX1_src_app_new
andXXX1_dst_app_new
, previuos top migrations areXXX0_src_app_old
andXXX0_dst_app_old
.Add an operation that renames table for
MoveMe
model and renames its app_label in ProjectState toXXX1_dst_app_new
. Do not forget to add dependency onXXX0_src_app_old
migration. The resultingXXX1_dst_app_new
migration is:Add dependency on
XXX1_dst_app_new
toXXX1_src_app_new
.XXX1_src_app_new
is no-op migration that is needed to make sure that futuresrc_app
migrations will be executed afterXXX1_dst_app_new
.Move
MoveMe
fromsrc_app/models.py
todst_app/models.py
. Then run:That's all!
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:
ForeignKey
linking toTheModel
intoIntegerfield
. Then runpython manage.py makemigrations
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 :)
Copied from my answer at https://stackoverflow.com/a/47392970/8971048
In case you need to move the model and you don't have access to the app anymore (or you don't want the access), you can create a new Operation and consider to create a new model only if the migrated model does not exist.
In this example I am passing 'MyModel' from old_app to myapp.
You can try the following (untested):
src_app
todest_app
dest_app
; make sure the schema migration depends on the latestsrc_app
migration (https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files)dest_app
, that copies all data fromsrc_app
src_app
; make sure the schema migration depends on the latest (data) migration ofdest_app
-- that is: the migration of step 3Note that you will be copying the whole table, instead of moving it, but that way both apps don't have to touch a table that belongs to the other app, which I think is more important.
Lets say you are moving model TheModel from app_a to app_b.
An alternate solution is to alter the existing migrations by hand. The idea is that each time you see an operation altering TheModel in app_a's migrations, you copy that operation to the end of app_b's initial migration. And each time you see a reference 'app_a.TheModel' in app_a's migrations, you change it to 'app_b.TheModel'.
I just did this for an existing project, where I wanted to extract a certain model to an reusable app. The procedure went smoothly. I guess things would be much harder if there were references from app_b to app_a. Also, I had a manually defined Meta.db_table for my model which might have helped.
Notably you will end up with altered migration history. This doesn't matter, even if you have a database with the original migrations applied. If both the original and the rewritten migrations end up with the same database schema, then such rewrite should be OK.
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
add this to app2.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:)
table=None, means it will take the default table-name, which in this case will be app2_yourmodel.
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.