3

Django migrations has excellent behavior in terms of single migrations, where, assuming you leave atomic=True, a migration will be all-or-nothing: it will either run to completion or undo everything.

Is there a way to get this all-or-nothing behavior for multiple migrations? That is to say, is there a way to either run multiple migrations inside an enclosing transaction (which admittedly could cause other problems) or to rollback all succeeded migrations on failure?

For context, I'm looking for a single command or setting to do this so that I can include it in a deploy script. Currently, the only part of my deploy that isn't rolled back in the event of a failure are database changes. I know that this can be manually done by running python manage.py migrate APP_NAME MIGRATION_NUMBER in the event of a failure, but this requires knowledge of the last run migration on each app.

Zags
  • 37,389
  • 14
  • 105
  • 140
  • If I'm understanding your question correctly, there was some [discussion](https://code.djangoproject.com/ticket/24535) back in 2015 about supporting this, along with some reasoning about why it wasn't included as a feature. I think it's a good question though, and something that should be reconsidered for certain databases that support transactional DDL. – Nick Merrill Aug 30 '22 at 22:35

1 Answers1

1

This isn't a feature in Django (at least as of 4.1). To do this yourself, your code needs to do the following:

  1. Get a list of current apps that have migrations. This can be done by getting the intersection of apps from settings.INSTALLED_APPS and the apps in the MigrationRecorder.
  2. Get the latest migration for each app.
  3. Run migrations.
  4. If migrations fail, run the rollback command for the latest migration for each app.

Here's an implementation of this as a custom management command: https://github.com/zagaran/django-migrate-or-rollback. This is done as a child-class of the migrate management command so that it exposes all of the command line options of migrate. To use this library, pip install django-migrate-or-rollback and add "django_migrate_or_rollback" to your INSTALLED_APPS in settings.py. Then you can use python manage.py migrate_or_rollback in place of the traditional migrate command.

Disclosure: I am the author of the library referenced (though I made the library based on this StackOverflow answer).

Zags
  • 37,389
  • 14
  • 105
  • 140