The right way out of this mistake is to recover from a backup. But let’s suppose I didn’t have a recent backup of my development database… because I didn’t.
So, here’s the general technique:
Let’s say you are stuck at a migration, and can’t move forward. Migrate to one you can reach. Example:
manage.py migrate appname 0018
Delete all the migrations after that.
Grep for your tablename in the migrations directory:
grep Report *
You will see where it’s been created, and altered. You will need to edit these migrations so that the initial migration that created it matches your model.
You will need to try and edit the migrations afterwards, too – but this isn’t strictly necessary in my experience… so don’t bother at first.
Copy the migration that created the table (with your edits) to a new migration:
cp 0001_initial.py 0019_report_restore.py
Edit the new file to delete everything except the migration to create your model.
Edit the “dependency” list so that you create a dependency on the previous migration. If migration 0018 is named “0018_auto_20150411_0205.py”, then the dependencies look like this (my appname is v1):
dependencies = [ ('v1', '0018_auto_20150411_0205'), ]
A fake migration file is attached. It’s not the one I used, but it’s an example of the syntax.
You can then run this migration:
manage.py migrate appname 0019
Then, you need to delete this migration, and do a makemigration:
That will combine all the new model changes, and create the migration. Apply the migration:
If it fails (and mine did) look at the error message and make adjustments. You may have not altered the initial migration file to match the model, completely. If that’s the case, alter the initial migration that created the table until it matches. Try migrating again.
At this point, it becomes a little tricky – you may need to delete the intermediate migrations that altered this table, too. It’s even scarier than it sounds, but you should feel pretty comfortable looking at the migration file by now.
In the end, your code will be slightly altered so it appears as if today’s model was initalized way back in the past.
One other thing – if you were having problems migrating in the first place, your database tables were probably out of whack. What I ended up doing was putting DROP TABLE and ALTER TABLE statements into a pane in MySQL Workbench, and running them to get a fresh start.
I wish I could end this with an encouraging paragraph about how great migrations are… but that’s not how I’m feeling. I think that the Django style of centralizing all the changes in the code is kind of ass backwards. The database should be the central repository of truth about the data. Generating code that can alter a table to match a Django model sounds dangerous, because programmers are a lot quicker to change a model than to change a database table. A small error in code can turn into a migration that alters the data.
I think a smarter way to migrate is by creating migration scripts by examining table schemas, and generating SQL code to perform the changes.
Am I going to do that? NO. I’m using a framework to build an app. The app is the goal, not writing an extension to this feature-rich framework. It’s not worth the effort to diverge from the system that’s already in place, and which works adequately most of the time. If something goes wrong… I’ll search for a solution and find this page, and follow the instructions 🙂