def test_alter_column_null(self): def null_ok(): from django.db import connection, transaction # the DBAPI introspection module fails on postgres NULLs. cursor = connection.cursor() try: cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, 10.1, now(), NULL);") except: transaction.rollback() return False else: cursor.execute("DELETE FROM southtest_spam") transaction.commit() return True app = migration.get_app("fakeapp") tree = migration.dependency_tree() self.assertEqual(list(migration.MigrationHistory.objects.all()), []) # by default name is NOT NULL migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0) self.failIf(null_ok()) # after 0003, it should be NULL migration.migrate_app(app, tree, target_name="0003", resolve_mode=None, fake=False, verbosity=0) self.assert_(null_ok()) # make sure it is NOT NULL again migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0) self.failIf(null_ok(), 'name not null after migration') # finish with no migrations, otherwise other tests fail... migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0) self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
def test_migration_merge_forwards(self): migration.MigrationHistory.objects.all().delete() app = migration.get_app("fakeapp") # We should start with no migrations self.assertEqual(list(migration.MigrationHistory.objects.all()), []) # Insert one in the wrong order migration.MigrationHistory.objects.create( app_name = "fakeapp", migration = "0002_eggs", applied = datetime.datetime.now(), ) # Did it go in? self.assertListEqual( ( (u"fakeapp", u"0002_eggs"), ), migration.MigrationHistory.objects.values_list("app_name", "migration"), ) # Apply them normally tree = migration.dependency_tree() try: # Redirect the error it will print to nowhere stdout, sys.stdout = sys.stdout, StringIO.StringIO() migration.migrate_app(app, tree, target_name=None, resolve_mode=None, fake=False, verbosity=0) sys.stdout = stdout except SystemExit: pass # Nothing should have changed (no merge mode!) self.assertListEqual( ( (u"fakeapp", u"0002_eggs"), ), migration.MigrationHistory.objects.values_list("app_name", "migration"), ) # Apply with merge migration.migrate_app(app, tree, target_name=None, resolve_mode="merge", fake=False, verbosity=0) # We should finish with all migrations self.assertListEqual( ( (u"fakeapp", u"0001_spam"), (u"fakeapp", u"0002_eggs"), (u"fakeapp", u"0003_alter_spam"), ), migration.MigrationHistory.objects.values_list("app_name", "migration"), ) # Now roll them backwards migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0) migration.migrate_app(app, tree, target_name="0001", resolve_mode=None, fake=True, verbosity=0) migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0) # Finish with none self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
def test_apply_migrations(self): migration.MigrationHistory.objects.all().delete() app = migration.get_app("fakeapp") # We should start with no migrations self.assertEqual(list(migration.MigrationHistory.objects.all()), []) # Apply them normally tree = migration.dependency_tree() migration.migrate_app(app, tree, target_name=None, resolve_mode=None, fake=False, verbosity=0) # We should finish with all migrations self.assertListEqual( ( (u"fakeapp", u"0001_spam"), (u"fakeapp", u"0002_eggs"), (u"fakeapp", u"0003_alter_spam"), ), migration.MigrationHistory.objects.values_list("app_name", "migration"), ) # Now roll them backwards migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0) # Finish with none self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
def test_dependencies(self): fakeapp = migration.get_app("fakeapp") otherfakeapp = migration.get_app("otherfakeapp") # Test a simple path tree = migration.dependency_tree() self.assertEqual( map( snd, migration.needed_before_forwards(tree, fakeapp, "0003_alter_spam")), ['0001_spam', '0002_eggs'], ) # And a complex one, with both back and forwards deps self.assertEqual( map( snd, migration.needed_before_forwards(tree, otherfakeapp, "0003_third")), [ '0001_spam', '0001_first', '0002_second', '0002_eggs', '0003_alter_spam' ], )
def test_dependencies(self): fakeapp = migration.get_app("fakeapp") otherfakeapp = migration.get_app("otherfakeapp") # Test a simple path tree = migration.dependency_tree() self.assertEqual( map(snd, migration.needed_before_forwards(tree, fakeapp, "0003_alter_spam")), ['0001_spam', '0002_eggs'], ) # And a complex one, with both back and forwards deps self.assertEqual( map(snd, migration.needed_before_forwards(tree, otherfakeapp, "0003_third")), ['0001_spam', '0001_first', '0002_second', '0002_eggs', '0003_alter_spam'], )
class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option('--all', action='store_true', dest='all_apps', default=False, help='Run the specified migration for all apps.'), make_option('--list', action='store_true', dest='list', default=False, help='List migrations noting those that have been applied'), make_option('--skip', action='store_true', dest='skip', default=False, help='Will skip over out-of-order missing migrations'), make_option('--merge', action='store_true', dest='merge', default=False, help='Will run out-of-order missing migrations as they are - no rollbacks.'), make_option('--no-initial-data', action='store_true', dest='no_initial_data', default=False, help='Skips loading initial data if specified.'), make_option('--fake', action='store_true', dest='fake', default=False, help="Pretends to do the migrations, but doesn't actually execute them."), make_option('--db-dry-run', action='store_true', dest='db_dry_run', default=False, help="Doesn't execute the SQL generated by the db methods, and doesn't store a record that the migration(s) occurred. Useful to test migrations before applying them."), ) if '--verbosity' not in [opt.get_opt_string() for opt in BaseCommand.option_list]: option_list += ( make_option('--verbosity', action='store', dest='verbosity', default='1', type='choice', choices=['0', '1', '2'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), ) help = "Runs migrations for all apps." args = "[appname] [migrationname|zero] [--all] [--list] [--skip] [--merge] [--no-initial-data] [--fake] [--db-dry-run]" def handle(self, app=None, target=None, skip=False, merge=False, backwards=False, fake=False, db_dry_run=False, list=False, **options): # Work out what the resolve mode is resolve_mode = merge and "merge" or (skip and "skip" or None) # NOTE: THIS IS DUPLICATED FROM django.core.management.commands.syncdb # This code imports any module named 'management' in INSTALLED_APPS. # The 'management' module is the preferred way of listening to post_syncdb # signals, and since we're sending those out with create_table migrations, # we need apps to behave correctly. for app_name in settings.INSTALLED_APPS: try: __import__(app_name + '.management', {}, {}, ['']) except ImportError, exc: msg = exc.args[0] if not msg.startswith('No module named') or 'management' not in msg: raise # END DJANGO DUPE CODE # if all_apps flag is set, shift app over to target if options.get('all_apps', False): target = app app = None # Migrate each app if app: apps = [migration.get_app(app.split(".")[-1])] if apps == [None]: print "The app '%s' does not appear to use migrations." % app print "./manage.py migrate " + self.args return else: apps = migration.get_migrated_apps() if list and apps: list_migrations(apps) if not list: tree = migration.dependency_tree() for app in apps: result = migration.migrate_app( app, tree, resolve_mode = resolve_mode, target_name = target, fake = fake, db_dry_run = db_dry_run, verbosity = int(options.get('verbosity', 0)), load_inital_data = not options.get('no_initial_data', False), skip = skip, ) if result is False: return