I agree that downgrade migrations are a waste of time for the most part, and Django’s migrations have lots of other painful pitfalls. I intend to write up a blog post about it what I’ve run into as well, but don’t know when I’ll get around to it.
Specifically regarding data migrations: Can you still use the models when you write code-based migrations? I seldom need them (I opt for SQL data migrations 99% of the time), in the past year I think I’ve done it once for quite complex data (which one could argue was overly complex). It might’ve been possible to write this in SQL directly, but sometimes doing it in code is a bit easier. Django’s migrations make it possible to use the ORM models (without any custom methods) as they were at the point in time the migration was written.
Regarding data migrations - it depends on the migration system that you’re using. The tool I’m using (nomad) supports any executables and will pass everything that’s needed as environment variables. So you can just drop your python file there that will call your main application and do what’s needed.
I’ve used this ability exactly zero times :) My database is pretty big (1.5TB now), so doing migrations in code can be very slow and very painful.
Good point brought up by this article. I too, would rather have a standard, really good migration tool to use on any project with a SQL database, than a different tool for Django vs Rails vs Ecto vs whatever else.
Regarding downgrade migrations, they are mostly valuable for when you are iterating on your migration code. If it’s easy to write your downgrade (or provided automatically) it is usually faster to undo your migration, modify it, then run it again, than to restore your database from a backup and try your migration again. Database transactions are great (and table stakes for a migration tool) but often I realize I missed something (e.g. misspelled a column) long after the transaction has completed and I’d prefer not to have migration that looks like “Step 1: create a a column. Step 2:… Step 6: rename that column because I spelled it wrong”.
Although, I would say, just as I prioritize convenience and don’t bother writing a downgrade migration unless it’s trivial, I also prioritize convenience and happily use my ORM models in migrations even though that comes with sharp edges (e.g. model method changes making your migration code now be wrong).
If I misspelled a column name in a migration that’s being developed and haven’t been deployed to stage or production, then I blatantly rename the field manually without any migrations and fix the typo in a migration. Call it “rebase migrations” ;)
I haven’t ever restored my dev DB from backup because of an error in a migration, it’s too long to wait.
We also don’t have model methods! Like, at all. Apart from repr, which I don’t count as a real method.