You are using south wrong

If you are a hackernews fellow like me and if you like Django and South, you should have read a post about one of my former colleague about south common pitfall. That’s a great post about all the mistakes people usually do when using south on their project. If you now apply that and your project grows, you might still do one of the major issue I use to do when using this migration tool.

On our actual project, we reached 317 migrations and that’s clearly too much for a project of that size. The impact is huge in the development process, you can’t run anymore the test suite with migrations on without waiting for minutes.

Here is why that happens.

South and the development workflow

Let’s take an almost real example: A blog post.

Let’s imagine you start from scratch.

You start with the model:

class Post(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()

As you are a rigorous developer, you create a migration for this:

python manage.py schemamigration post --init
---> create 0001_initial.py

python manage.py migrate post

You write some views, some tests, some model methods, etc…

Then you notice you’d like to have an image associated with a blog post.

You add an ImageField:

    image = models.ImageField(upload_to="images")

and, still rigorous, you create a migration:

python manage.py schemamigration post --auto
---> create 0002_add_field_image.py

python manage.py migrate post

You spend 30 seconds remembering how boring it was to delete the table or to migrate it manually when you use to use syncdb only.

YOU DID IT WRONG!

You have 2 migrations where you should have only one. Your migration should transform your Production / Tests database from one state to another, not reflect all the development iterations you did. You should have merged these migrations. There is no reason to migrate a table which doesn’t already exist in production.

If you are alone on your project or if you project is only a few days long, that’s ok. If you have 5 people working on it for months, that’s not.

A better workflow

You still start with the model, create views and tests:

class Post(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()

You still create the migration because, at that time, you don’t know you would add something later:

python manage.py schemamigration post --init
---> create 0001_initial.py

python manage.py migrate post

You still notice image is missing and add it to the model You add an ImageField:

    image = models.ImageField(upload_to="images")

THEN, you remove the existing migration:

rm ./post/migrations/0001_initial.py

You create a new initial migration:

python manage.py schemamigration post --init
---> create 0001_initial.py

You drop the existing table:

python manage.py dbshell

>>> drop table post_post;

You delete the migration history

python manage.by shell_plus

>>> MigrationHistory.objects.get(app_name="post").delete()

Then you run the migration:

python manage.py migrate post

This way, you don’t get 300+ migrations when less than 100 would have been sufficient. You “rebase” your migrations ;)

Published: March 15 2013

blog comments powered by Disqus