Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django: revert merge migration

Let's suppose we have migrations with the following dependency graph (all applied): Initial state

Now, for some reason we want to revert database schema to the state after applying migration 0006_f. We type:

./manage.py migrate myapp 0006_f

and now we have the following state: One branch reverted

The problem is that Django does not revert the right branch, so now we have some migrations applied from left branch and some from the right one.

One way to avoid it is to migrate back to 0002_b and forward to 0006_f but this can cause data loss. Also some of migrations 0006_f, 0005_e, 0004_d, 0003_c can be irreversible.

Another way is to run the following:

./manage.py migrate myapp 0006_f
./manage.py migrate myapp 0004_d1

Now, to achieve the desired state we only have to revert the migration 0004_d1 and I do not see a way to undo 0004_d1 without undoing 0006_f, 0005_e and 0004_d except for opening DB shell and reverting it manually.

Is there a way to explicitly undo only one migration? Is there another way to properly undo migrations from parallel branch? Is there some reason for Django not to automatically revert migrations from parallel branch when undoing merge migration?

like image 352
Kirill Gagarski Avatar asked Mar 28 '17 19:03

Kirill Gagarski


People also ask

How do I flush migrations in Django?

Django's migration can be reset by cleaning all the migration files except __init__.py files under each project app directory, followed by dropping the database and creating migration again using python manage.py makemigrations and python manage.py migrate .

How does Django resolve conflicting migrations?

you can either: temporarily remove your migration, execute python manage.py migrate, add again your migration and re-execute python manage.py migrate. Use this case if the migrations refer to different models and you want different migration files for each one of them.

Why Makemigrations is not working?

This may happen due to the following reasons: You did not add the app in INSTALLED_APPS list in settings.py (You have to add either the app name or the dotted path to the subclass of AppConfig in apps.py in the app folder, depending on the version of django you are using). Refer documentation: INSTALLED_APPS.


1 Answers

edited 2019-10-17: I'm adding a step 0 which I've found reduces some risks when cross-app dependencies are involved.

If I'm reading your question correctly, you had a situation similar to mine where I wanted to revert one specific branch without touching the others.

I managed to do this (in v1.11.7, and v2.2) with these steps:

  1. reverse migrate to the first node of each branch you want to un-apply (0004_d1)
  2. fake a reverse migration to the common ancestor (0003_c) edit: see note at bottom
  3. fake migrations to the first node of each branch you want to revert (0004_d1)
  4. reverse migrate to the common ancestor from step 1 (0003_c)
  5. fake a migration to the tip of the branch you wanted to preserve (0007_g)
  6. delete or modify the merge migration as needed; see below (0008_merge)

So in your situation:

./manage.py migrate 0004_d1
./manage.py migrate --fake myapp 0003_c
./manage.py migrate --fake myapp 0004_d1
./manage.py migrate myapp 0003_c
./manage.py migrate --fake myapp 0007_g

If your merge migration 0008_merge does any actual work or migrates more branches, you'll probably have to edit it manually to omit the 0005_e1 branch and then fake-migrate to it; otherwise you should be able to just delete it.

edit: note regarding step 2: It seems that sometimes, if step 2 causes some cross-app dependency migrations to be fake-unapplied, they may cause step 4 to fail. Step 1 mitigates this risk, but you should check what operations have been applied and try to make sure any cross-app dependencies are not in a faked state during step 4.

like image 181
Tuomo Mattila Avatar answered Oct 17 '22 00:10

Tuomo Mattila