Search code examples
djangodvcsdjango-south

Django South - Any way to detect if south migration needs to be done?


Say you are developing on a Django project with a few people using (for example) git. When you do git pull you might get some South migrations, however, you might not notice (for some reason). Then when you go on developing, you might run into python exceptions because you didn't do the migrations. Now sometimes it can be some time before you find out you forgot this, which is pretty annoying.

So therefor I was thinking, can't South detect that you haven't done all the migrations and just throw an exception if so?

I imagen this could be a setting which you could turn off if you want to keep developing without doing the migration.


Solution

  • Ok I made some middleware which does what I want. When you do a request it checks which migrations you haven't applied yet and raises an exception. Be sure to use this middleware only in development!

    You can also find the source on https://gist.github.com/gitaarik/5974176

    from south import migration
    from south.models import MigrationHistory
    
    
    class SouthUnranMigrationCheck(object):
    
        def process_request(self, request):
            '''
            Checks if you ran all South migrations. If not, it will throw an
            exception (DidNotApplyAllMigrations).
            '''
    
            unapplied_migrations = self.unapplied_migrations()
    
            if len(unapplied_migrations) > 0:
    
                message = u'You haven\'t run the following migrations: {}'.format(
                    u''.join(
                        [u'\n  "{}" in app "{}".'.format(name, app)
                        for name, app in unapplied_migrations]
                    )
                )
    
                raise DidNotApplyAllMigrations(message)
    
        def unapplied_migrations(self):
            '''
            Returns a list of tuples of unapplied migrations. The tuples consist of
            a migration name and an app label.
            '''
    
            applied_migrations = self.applied_migrations()
            unapplied_migrations = []
    
            for app_migration_files in migration.all_migrations():
    
                for migration_file in app_migration_files:
    
                    app_label = migration_file.app_label()
                    migration_name = migration_file.name()
    
                    if migration_name not in applied_migrations[app_label]:
                        unapplied_migrations.append((migration_name, app_label))
    
            return unapplied_migrations
    
        def applied_migrations(self):
            '''
            Returns a dictionary with the app name in the key, and a list of
            migrations in the value.
            '''
    
            applied_migrations = {}
    
            for applied_migration in MigrationHistory.objects.all():
    
                if applied_migration.app_name not in applied_migrations:
                    applied_migrations[applied_migration.app_name] = []
    
                applied_migrations[applied_migration.app_name].append(
                    applied_migration.migration)
    
            return applied_migrations
    
    
    class DidNotApplyAllMigrations(Exception):
        '''
        Exception that indicates that you havent run all migrations.
        '''
        pass