Productively Distracted
Posted Monday March 21, 2011 around 11:15 PM

I love django-south. I use it on all of my projects and I have never had a single issue with it. Today however, I needed to restart my development database from scratch. I thought that this should be a trivial matter, but it has taken me the last 3 hours to fix my south migrations up to the point that I can go from nothing to a fully populated database. This snuck up on me because I have the setting SOUTHTESTSMIGRATE to false to speed up my unit testing cycle.

Migrations are run per app

When I started using south, I assumed that migrations would be run in order of creation much like ruby on rails does it. As it turns out south runs them straight through per app. This works out just fine unless your apps have cross dependencies. So for example if you have 2 apps in your project. Lets say blog and account. The migrations will run completely thru for blog and then they will run completely through for account (or vice versa). This means that if you have a migration in blog that relies on a field that was added later on to account, you will get errors. This also means that if you have initial data in a fixture file, that file will attempt to load at the end of each app run. If the initial data is only for the current app that was just migrated, then there will be no problem. But if the data is for multiple apps you will get errors.

Solution

Models

To account for this, south includes a dependencies attribute that can be added to the DataMigration class. In this case the blog migration would look something like this:

class Migration(DataMigration):
    depends_on = (
        ("account", "0002_auto__add_field_that_is_needed"),
    )

    def forwards(self, orm):
        # Code ...
    def backwards(self, orm):
        # Code ...
Download

I highly recommend accounting for these as you go along. Adding them after the fact is misrable work.

Initial Data

Initial data is a bit easier. The key is to stop using the django auto loading feature and manually run the fixtures yourself. The django-south documentation has a good example of this that you can find here. The basic idea is to call django.core.management.call_command directly in a datamigration that depends on the minimum database migration that you need. So you would run the command:

$ python manage.py datamigration blog initial_data_one
Download

Then implement something like this in the new datamigration:

from django.core.management import call_command

class Migration(DataMigration):
    depends_on = (
        ("account", "0002_auto__add_field_that_is_needed"),
    )

    def forwards(self, orm):
        call_command("loaddata", "my_initial_data.json")
Download

Conclusion

This is not new or special information. It can be found in the south documentation without much trouble. However it is a little buried and not terribly intuitive until you run into an issue. Basically it's what I wish I had known when I started out with django-south.

blog comments powered by Disqus