Search code examples
pythondjangodjango-south

Django South: detecting if fake migrations were applied in production


I'm adding South to an existing application which has many installations however I don't have access to production environments due to security reasons.
We can only supply Python installation script that will be run by persons that often don't have any knowledge about Django, South, etc.

I'm aware of the fact that for existing installations any future upgrade will have to start with executing:

manage.py syncdb
manage.py migrate --all 0001 --fake

and any new installations will start with:

manage.py syncdb
manage.py migrate -all


Is there any way to detect if south initial migrations were already applied (e.g. by detecting if south_migfationhistory table exists) in a database agnostic way (perhaps with Django itself) ?
What I would like to do is:

(pseudocode)
db = database.connect(dbname, user, password)
if db.table_existst('south_migrationhistory'):
  execute 'manage.py syncdb'
  execute 'manage.py migrate --all'
else:
  execute 'manage.py syncdb'
  execute 'manage.py migrate --all 0001 --fake'
  execute 'manage.py migrate --all'

Solution

  • For future reference this is how I ended up doing this:

    if args.settings_dir not in sys.path:
      sys.path.append(args.settings_dir)
    
    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
    
    #try to query db for existing objects, e.g. user groups
    #in order to detect if we are upgrading an existing installation
    from django.db.utils import DatabaseError
    try:
      from django.contrib.auth.models import Group
      tmp = len(Group.objects.all()) #evaluate to force db query
      updating = True
    except DatabaseError as e:
      updating = False
    
    #try to query db for South migrations in order to detect if South was already applied
    #in this installation (if not then accessing MigrationHistory will throw an excepion)
    try:
      from south.models import MigrationHistory
      has_south = bool(len(MigrationHistory.objects.all()))
    except ImportError as e:
      print 'ERROR: Error importing South migration history: ' + str(e)
      print 'Exiting'
      exit(1)
    except DatabaseError as e:
      has_south = False
    
    #syncdb to create south_migrationhistory table, portal models will not be synced
    from django.core.management import execute_from_command_line
    argv = ['manage.py', 'syncdb', '--settings=settings', '--pythonpath=' + args.settings_dir]
    execute_from_command_line(argv)
    
    #if we are updating existing installation and South wasn't already applied
    #then initial migration has to be 'faked' in order to sync with existing tables
    if updating and not has_south:
      print 'INFO: Faking initial database migration...'
      argv = ['manage.py', 'migrate', '--all', '0001', '--fake',
              '--settings=settings', '--pythonpath=' + args.settings_dir]
      execute_from_command_line(argv)
    
    #run normal migrations
    print 'INFO: Applying database migrations...'
    argv = ['manage.py', 'migrate', '--all',
            '--settings=settings', '--pythonpath=' + args.settings_dir]
    execute_from_command_line(argv)