I really like django, but one thing it lacks is db schema migration. I’m trying out a django package called “south” that does that. (Debian package python-django-south).

South keeps track of migrations applied by adding a table to your database called south_migrationhistory. Of course, this table has to be added before south will work … you add it using syncdb. Fortunately, south keeps track of what it does and what syncdb does.

So you start using south by installing south, and then running ./manage.py syncdb. Now you have some new ./manage.py commands, called startmigration and migrate (also datamigration, schemamigration and graphmigrations, maybe I’ll look at those another time). To snapshot your initial db state or to detect changes to your schema, you use the startmigration command. To apply migrations to your database, you use the migrate command.

To snapshot your initial db state, use startmigration --initial. To detect changes to your schema, use startmigration --auto. The startmigration command will, in addition to other things, dump out the current db schema into the migration file. Every generated migration file contains a schema declaration towards the end. It also contains a migrate forwards and a migrate backwards command, for applying the migration and unapplying it automatically. The schema changes are detected by comparing the model data declarations with the migration schema dumps.

Some examples, for a db named clientportal and an app named portal:

# create migration named portal/migrations/0001_initial.py
./manage.py startmigration portal initial --initial

The initial migration is already in your database, so you don’t have to apply it yourself. Then you edit the portal/models.py file, and add a field. Then you can have south detect this and create a migration that applies the change to the database.

./manage.py startmigration portal add_field --auto  

You can use the newly created migration to change the database:

# will find all migrations named 0002_add_field
# and apply them (in alpha order of app name)
./manage.py migrate 0002_add_field 

I’m not sure how to better control the migrating naming and order of application. For instance, it seems that migrations are numbered sequentially within applications, but you don’t specify the number. So if you have more than one app (app a and app b), and you create a migrations in this order:

b/migration/0001_initial.py
a/migration/0001_initial.py
b/migration/0002_add_field.py

then you run ./manage.py migrate, the migrations will be applied in this order:

a/migration/0001_initial.py
b/migration/0001_initial.py
b/migration/0002_add_field.py

And I’m afraid that if I made a new migration (add_another_field) for app a, it would be called 0002_add_another_field and would be applied with all the other migrations (on ./manage.py migrate on a new db):

a/migration/0001_initial.py
a/migration/0002_add_another_field.py
b/migration/0001_initial.py
b/migration/0002_add_field.py

A little annoying when the migrations should have been applied in this order:

b/migration/0001_initial.py
a/migration/0001_initial.py
b/migration/0002_add_field.py
a/migration/0002_add_another_field.py

Hopefully there is a way to handle it, even if I don’t yet know what it is. Just being able to specify the 000n numbers would do it.

To list out the available migrations, you can ./manage.py migrate --list. The migrations that have been installed have a * next to them.

UPDATE 2011/Jan/12: to revert to an old version: ./manage.py migrate appname 0006_shortname_not_null

brings you to the state just after 0006_shortname_not_null has been applied.