South: how to revert migrations in production server?
Asked Answered
H

3

6

I want to revert my last migration (0157) by running its Migration.backwards() method. Since I am reverting the migration in production server I want to run it automatically during code deployment. Deployment script executes these steps:

  1. Pull code changes
  2. Run migrations: manage.py migrate <app>
  3. Refresh Apache to use newest code: touch django.wsgi

If I could, I would create new migration file which would tell South to backward migrate to 0156:

migrations/0158_backward__migrate_to_0156.py

This commited migration would be deployed to production and executed during manage.py migrate <app> command. In this case I wouldn't have to execute backward migration by hand, like suggested in these answers.

Lets say, I have created two data migrations, first for user's Payment, second for User model. I have implemented backwards() methods for both migrations in case I'd have to revert these data migrations. I've deployed these two migrations to production. And suddenly find out that Payment migration contains an error. I want to revert my two last data migrations as fast as possible. What is the fastest safe way to do it?

Humectant answered 23/12, 2013 at 17:0 Comment(2)
possible duplicate of Backwards migration with Django SouthOboe
Its not, because this question do not require a separate backward migration file. All solutions suggest running backward migration by hand.Humectant
S
4

Since I am reverting the migration in production server I want to run it automatically during code deployment.

IMHO the safest path is

  1. run manage.py migrate <app> (i.e. apply all existing migrations, i.e. up to 0156)
  2. undo the changes in your model
  3. run manage.py schemamigration <app> --auto

This will create a new migration 0157 that effectively reverts the previous migration 0156. Then simply apply the new migration by running manage.py migrate <app> again. As I understand, your code deployment will just do that.

Sexpartite answered 26/1, 2014 at 0:49 Comment(5)
I prefer to use Migration.backwards() method to revert the migration, instead of creating completely new migration. This is needed for datamigration migrations, which are implemented by hand.Humectant
@Humectant please clarify your question, where you say " but I want to run it as new separate migration. " You cannot run backwards() by running it as a seperate forward migration, backwards() are only run if you go back (i.e. migrate to a version lower than the current version)Sexpartite
@nikas I stick to my answer. Don't worry if it creates a forwards() migration, because essentially that's what you want.Sexpartite
But how about this scenario: I have created two data migrations, first for user's Payment, second for User model. I have implemented backwards() methods for both migrations in case I'd have to revert these data migrations. I've deployed these two migrations to production. And suddenly find out that Payment migration contains an error. I want to revert my two last data migrations as fast as possible. With your solution I would have to export backwards() logic from both migrations to the new one, which would take a while and copying code could potentially cause an error.Humectant
I see. Other than chaning the deployment process to process specific migrations (so you could ask it to run, say, migration 0157, I don't see any other way than this. You might be able to write a forwards() migration that simply calls another migration's backwards(), but I wouldn't consider that safe, as it breaks the way south works.Sexpartite
H
2

Apparently the codeline has migrations up to #157 and now the developer decided that the last one was not a good idea after all. So the plan is to go back to #156.

Two scenarios:

(a) migration #157 was not released or deployed anywhere yet. Simply revert the last change from models.py and delete migration #157.py from the source archive. Any deployment will take the system to level 156; "157 was never there".

(b) there have been deployments of the latest software with migration #157. In this case the previous strategy will obviously not work. So you need to create a migration #158 to undo #157. Revert the change in models.py and run

django manage.py migrate <app> 0157
django manage.py schemamigration <app> --auto

This will auto-generate a new migration #158, which will contain the inverse schema migration compared to #157.

If schemamigration is giving trouble because of django Model validation (something that can happen if you have custom validators which check stuff outside the ORM box), I suggest the following workaround:

<django project>/<app>/management/commands/checkmigrations.py

from south.management.commands import schemamigration
class Command(schemamigration.Command):
    requires_model_validation = False
    help = "schemamigration without model validation"

This command becomes available in manage.py:

django manage.py checkmigrations <app> --auto
Hobo answered 23/1, 2014 at 13:27 Comment(2)
Did you mean manage.py migrationcheck <app>? These two code lines simply run the migrations without generating any files.Humectant
I didn't realize that checkmigrations is actually a custom command we created a long while ago :) I've edited the post on this subject, see above.Hobo
F
1

There's no silver bullet here. The simplest solution I can think of would be to - in your dev env of course - manually migrate back to 0156, manually update your migration's history table (sorry I can't remember the table's name now) to fool south in thinking you're still @0158, then run schemamigration again. Not garanteed to work but might be worth trying.

Foxed answered 23/12, 2013 at 18:56 Comment(4)
Doesn't seem right. What is the standard way to revert DB schema changes in production server without downtime?Humectant
What I was suggestion was to do this on your dev environnement, then deploy the newly created migration to your server. And there's no way to run a migration without any downtime.Foxed
I deploy code to production env, run migrations and touch django.wsgi. New migration gets commited and code refreshes almost at the same time. I though that there was no downtime (part of a second maybe).Humectant
Yes, the downtime can be quite short, but it does exist. Between the moment the migration is runned and the moment your processes are restarted you can have a couple 500 errors happening; depending on the traffic and what your migrations did. And the solution I suggested doesn't imply any manual tweaking of the migration's history on the production's db - only on dev.Foxed

© 2022 - 2024 — McMap. All rights reserved.