def test_nonpy_and_nonnumber_are_ignored(self): db = MigrationDb() db.populate_migrations_from_ls(["1_foo.py", "1_bar.pyc", "4-ohhi.py", "-9_kitty.py", "1_bar.html", ".", ".boo", "..", "123.py.gz", "hello_45.py", "__init__.py", "___init__.pyc", "hello.py", "hello.pyc"]) self.assert_equal([ "1_foo", ], db.list() )
def test_migrations_can_be_initialized_from_directory(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() self.assert_equal([ "001_foo", "002_bar", "005_DEV_hello", "009_one", "09_two", ], db.list() )
def test_dup_warnings(self): db = MigrationDb() db.warn = WarningsMocker() db.populate_migrations_from_ls([ "17_foo.py", "017_bar.py", "0018_foo.py", "00018_foo.py", "180_hello.py", ]) self.assert_equal( [ u'There are multiple migrations with the same number 17: 017_bar, 17_foo', u'There are multiple migrations with the same number 18: 00018_foo, 0018_foo' ], db.warn.warnings )
def test_load_migration_object(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() for name in ['001_foo', '0001', '001', '01', '1', 1]: self.assert_attrs(db.load_migration_object(name), name = '001_foo', dev = False, sql_up = "INSERT INTO mock VALUES (1)", ) for name in ['002_bar', '0002', '002', '02', '2', 2]: self.assert_attrs(db.load_migration_object(name), name = '002_bar', dev = False, sql_up = "INSERT INTO mock VALUES (2)", ) for name in ['005_DEV_hello', '0005', '005', '05', '5', 5]: self.assert_attrs(db.load_migration_object(name), name = '005_DEV_hello', dev = True, sql_up = "INSERT INTO mock VALUES (5)", ) for name in ['3', '02_foo', '2_foo', 3]: self.assert_raises(NoSuchMigrationError, lambda: db.load_migration_object(name))
def test_migrations_sorted_by_numbers_not_ascii(self): db = MigrationDb(migrations=[ "04_blah", "03_blah", "002_bar", "12_blah", "01_foo", "011_blah", ]) self.assert_equal([ "01_foo", "002_bar", "03_blah", "04_blah", "011_blah", "12_blah", ], db.list() )
def test_all_migrations_applied(self): db = MigrationDb(migrations = ['001_foo', '002_DEV_bar', '005_omg', '006_DEV_hello']) si = MigrationState(migration_db=db) si.init() si.mark_as_applied('005_omg') si.mark_as_applied('001_foo') si.mark_as_applied('009_bogus') self.assert_equal(['001_foo', '005_omg', '009_bogus'], si.all_migrations_applied())
def test_is_dev_migration(self): db = MigrationDb() self.assert_equal(False, db.is_dev_migration("1_foo")) self.assert_equal(False, db.is_dev_migration("1DEV_foo")) self.assert_equal(False, db.is_dev_migration("1_DEVELOPER_foo")) self.assert_equal(True, db.is_dev_migration("1_DEV_foo")) self.assert_equal(True, db.is_dev_migration("005_DEV_bar")) self.assert_equal(True, db.is_dev_migration("1__DEV__foo"))
def test_dups_and_missing_numbers_ok(self): db = MigrationDb(migrations = [ "9_sweet", "8_blah_one", "7_blah", "10_gah", "8_blah_three", "1_foo", "8_blah_two", ]) self.assert_equal([ "1_foo", "7_blah", "8_blah_one", "8_blah_three", "8_blah_two", "9_sweet", "10_gah", ], db.list() )
def test_when_doing_something_then_loggged(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() si = MigrationState(migration_db=db, dev=True) si.init() si.apply('001_foo') si.mark_as_unapplied('001_foo') si.apply('001_foo') si.mark_as_applied('005_omg') si.unapply('001_foo') self.assert_equal([ ("apply", "001_foo", "success"), ("mark_as_unapplied", "001_foo", "success"), ("apply", "001_foo", "success"), ("mark_as_applied", "005_omg", "success"), ("unapply", "001_foo", "success"), ], [row[:3] for row in get_log()]) self.assert_equal(True, isinstance(get_log()[0][3], datetime))
def test_find_unique_migration_by_number(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() self.assert_equal("001_foo", db.find_unique_migration_by_number(1)) self.assert_equal("002_bar", db.find_unique_migration_by_number(2)) self.assert_equal(None, db.find_unique_migration_by_number(3)) self.assert_raises(AmbiguousMigrationNameError, lambda: db.find_unique_migration_by_number(9))
def test_when_doing_something_then_loggged(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() si = MigrationState(migration_db=db, dev=True) si.init() si.apply("001_foo") si.mark_as_unapplied("001_foo") si.apply("001_foo") si.mark_as_applied("005_omg") si.unapply("001_foo") self.assert_equal( [ ("apply", "001_foo", "success"), ("mark_as_unapplied", "001_foo", "success"), ("apply", "001_foo", "success"), ("mark_as_applied", "005_omg", "success"), ("unapply", "001_foo", "success"), ], [row[:3] for row in get_log()], ) self.assert_equal(True, isinstance(get_log()[0][3], datetime))
def test_load_migration_object_with_dups(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() self.assert_attrs(db.load_migration_object('009_one'), name = '009_one', dev = False, sql_up = "INSERT INTO mock VALUES (9, 1)", ) self.assert_attrs(db.load_migration_object('09_two'), name = '09_two', dev = False, sql_up = "INSERT INTO mock VALUES (9, 2)", ) for name in ['0009', '009', '09', '9', 9]: self.assert_raises(AmbiguousMigrationNameError, lambda: db.load_migration_object(name))
def test_plan_with_dev(self): # NOTE: si.mark_as_applied modifies global state, not si (MigrationState) object. # There is only one meaningful MigrationState object per database, # so it's an almost-singleton unless we support multiple databases at the same time. # This makes testing quite ugly. db = MigrationDb(migrations = ['001_foo', '002_DEV_bar', '005_omg', '006_DEV_hello']) si = MigrationState(migration_db=db) si.init() self.assert_plans(si, ['up'], [('001_foo', 'up')], ['upto', '5'], [('001_foo', 'up'), ('005_omg', 'up')], ['upto', '6'], [('001_foo', 'up'), ('005_omg', 'up')], ['all'], [('001_foo', 'up'), ('005_omg', 'up')], ) si = MigrationState(migration_db=db, dev=True) si.init() self.assert_plans(si, ['up'], [('001_foo', 'up')], ['upto', '5'], [('001_foo', 'up'), ('002_DEV_bar', 'up'), ('005_omg', 'up')], ['upto', '6'], [('001_foo', 'up'), ('002_DEV_bar', 'up'), ('005_omg', 'up'), ('006_DEV_hello', 'up')], ['all'], [('001_foo', 'up'), ('002_DEV_bar', 'up'), ('005_omg', 'up'), ('006_DEV_hello', 'up')], ) si.mark_as_applied('001_foo') si = MigrationState(migration_db=db) si.init() self.assert_plans(si, ['up'], [('005_omg', 'up')], ['upto', '5'], [('005_omg', 'up')], ['upto', '6'], [('005_omg', 'up')], ['all'], [('005_omg', 'up')], ) si = MigrationState(migration_db=db, dev=True) si.init() self.assert_plans(si, ['up'], [('002_DEV_bar', 'up')], ['upto', '5'], [('002_DEV_bar', 'up'), ('005_omg', 'up')], ['upto', '6'], [('002_DEV_bar', 'up'), ('005_omg', 'up'), ('006_DEV_hello', 'up')], ['all'], [('002_DEV_bar', 'up'), ('005_omg', 'up'), ('006_DEV_hello', 'up')], ) si.mark_as_applied('002_DEV_bar') si = MigrationState(migration_db=db) si.init() self.assert_plans(si, ['up'], [('005_omg', 'up')], ['upto', '5'], [('005_omg', 'up')], ['upto', '6'], [('005_omg', 'up')], ['all'], [('005_omg', 'up')], ['down'], [('001_foo', 'down')], # Is it better to always assume --dev on down ? ) si = MigrationState(migration_db=db, dev=True) si.init() self.assert_plans(si, ['up'], [('005_omg', 'up')], ['upto', '5'], [('005_omg', 'up')], ['upto', '6'], [('005_omg', 'up'), ('006_DEV_hello', 'up')], ['all'], [('005_omg', 'up'), ('006_DEV_hello', 'up')], ['down'], [('002_DEV_bar', 'down')], )
def test_plan(self): db = MigrationDb(migrations = ['001_foo', '002_bar', '005_omg', '005_hello']) si = MigrationState(migration_db=db) si.init() self.assert_plans(si, ['apply', '1'], [('001_foo', 'up')], ['apply', '001_foo'], [('001_foo', 'up')], ['apply', '1', '2'], [('001_foo', 'up'), ('002_bar', 'up')], ['unapply', '001_foo'], [], ['unapply', '1', '2'], [], ['all'], [('001_foo', 'up'), ('002_bar', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['up'], [('001_foo', 'up')], ['down'], [], ['to', '1'], [('001_foo', 'up')], ['to', '2'], [('001_foo', 'up'), ('002_bar', 'up')], ['upto', '2'], [('001_foo', 'up'), ('002_bar', 'up')], ['upto', '002_bar'], [('001_foo', 'up'), ('002_bar', 'up')], ['upto', '4'], [('001_foo', 'up'), ('002_bar', 'up')], ['upto', '004_foo'], NoSuchMigrationError, ['upto', '5'], AmbiguousMigrationNameError, ['upto', '005_hello'], [('001_foo', 'up'), ('002_bar', 'up'), ('005_hello', 'up')], ['upto', '005_omg'], [('001_foo', 'up'), ('002_bar', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['downto', '1'], [], ['downto', '2'], [], ) si.mark_as_applied('002_bar') self.assert_plans(si, ['apply', '1'], [('001_foo', 'up')], ['apply', '001_foo'], [('001_foo', 'up')], ['apply', '1', '2'], [('001_foo', 'up')], ['unapply', '001_foo'], [], ['unapply', '1', '2'], [('002_bar', 'down')], ['all'], [('001_foo', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['up'], [('001_foo', 'up')], ['down'], [('002_bar', 'down')], ['to', '1'], [('002_bar', 'down'), ('001_foo', 'up')], ['to', '2'], [('001_foo', 'up')], ['upto', '2'], [('001_foo', 'up')], ['upto', '002_bar'], [('001_foo', 'up')], ['upto', '4'], [('001_foo', 'up')], ['upto', '004_foo'], NoSuchMigrationError, ['upto', '5'], AmbiguousMigrationNameError, ['upto', '005_hello'], [('001_foo', 'up'), ('005_hello', 'up')], ['upto', '005_omg'], [('001_foo', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['downto', '1'], [('002_bar', 'down')], ['downto', '2'], [], ) si.mark_as_applied('001_foo') self.assert_plans(si, ['apply', '1'], [], ['apply', '001_foo'], [], ['apply', '1', '2'], [], ['unapply', '001_foo'], [('001_foo', 'down')], ['unapply', '1', '2'], [('001_foo', 'down'), ('002_bar', 'down')], ['all'], [('005_hello', 'up'), ('005_omg', 'up')], ['up'], [('005_hello', 'up')], ['down'], [('002_bar', 'down')], ['to', '1'], [('002_bar', 'down')], ['to', '2'], [], ['upto', '2'], [], ['upto', '002_bar'], [], ['upto', '4'], [], ['upto', '004_foo'], NoSuchMigrationError, ['upto', '5'], AmbiguousMigrationNameError, ['upto', '005_hello'], [('005_hello', 'up')], ['upto', '005_omg'], [('005_hello', 'up'), ('005_omg', 'up')], ['downto', '1'], [('002_bar', 'down')], ['downto', '2'], [], ) si.mark_as_unapplied('002_bar') self.assert_plans(si, ['apply', '1'], [], ['apply', '001_foo'], [], ['apply', '1', '2'], [('002_bar', 'up')], ['unapply', '001_foo'], [('001_foo', 'down')], ['unapply', '1', '2'], [('001_foo', 'down')], ['all'], [('002_bar', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['up'], [('002_bar', 'up')], ['down'], [('001_foo', 'down')], ['to', '1'], [], ['to', '2'], [('002_bar', 'up')], ['upto', '2'], [('002_bar', 'up')], ['upto', '002_bar'], [('002_bar', 'up')], ['upto', '4'], [('002_bar', 'up')], ['upto', '004_foo'], NoSuchMigrationError, ['upto', '5'], AmbiguousMigrationNameError, ['upto', '005_hello'], [('002_bar', 'up'), ('005_hello', 'up')], ['upto', '005_omg'], [('002_bar', 'up'), ('005_hello', 'up'), ('005_omg', 'up')], ['downto', '1'], [], ['downto', '2'], [], ) si.mark_as_applied('002_bar') si.mark_as_applied('005_hello') si.mark_as_applied('005_omg') self.assert_plans(si, ['apply', '1'], [], ['apply', '001_foo'], [], ['apply', '1', '2'], [], ['unapply', '001_foo'], [('001_foo', 'down')], ['unapply', '1', '2'], [('001_foo', 'down'), ('002_bar', 'down')], ['all'], [], ['up'], [], ['down'], [('005_omg', 'down')], ['to', '1'], [('005_omg', 'down'), ('005_hello', 'down'), ('002_bar', 'down')], ['to', '2'], [('005_omg', 'down'), ('005_hello', 'down')], ['upto', '2'], [], ['upto', '002_bar'], [], ['upto', '4'], [], ['upto', '004_foo'], NoSuchMigrationError, ['upto', '5'], AmbiguousMigrationNameError, ['upto', '005_hello'], [], ['upto', '005_omg'], [], ['downto', '1'], [('005_omg', 'down'), ('005_hello', 'down'), ('002_bar', 'down')], ['downto', '2'], [('005_omg', 'down'), ('005_hello', 'down')], )
def handle(self, *args, **options): try: migrations_dir = settings.DMIGRATIONS_DIR except AttributeError: print "You need to add DMIGRATIONS_DIR to your settings" return migration_db = MigrationDb(directory = migrations_dir) migration_state = MigrationState( migration_db = migration_db, dev = options.get('dev') ) verbosity = int(options.get('verbosity', 1)) if not args or args[0] == 'help': self.print_help(sys.argv[0], 'dmigrate') return elif args[0] in 'all all_hard up down upto downto to apply unapply'.split(): migration_state.init() for (migration_name, action) in migration_state.plan(*args): migration = migration_db.load_migration_object(migration_name) start_time = time.time() if action == 'up': if verbosity >= 1: print "Applying migration %s" % migration.name if not options.get('print_plan'): migration_state.apply(migration_name) else: if verbosity >= 1: print "Unapplying migration %s" % migration.name if not options.get('print_plan'): migration_state.unapply(migration_name) if options.get('print_time'): print "Migration %s ran %.1f seconds" % (migration.name, time.time() - start_time) elif args[0] == 'mark_as_applied': migration_state.init() for name in args[1:]: resolved_name = migration_state.resolve_name(name) if resolved_name == None: raise NoSuchMigrationError(name) migration_state.mark_as_applied(resolved_name) elif args[0] == 'mark_as_unapplied': migration_state.init() for name in args[1:]: resolved_name = migration_state.resolve_name(name) if resolved_name == None: raise NoSuchMigrationError(name) migration_state.mark_as_unapplied(resolved_name) elif args[0] == 'list': migration_state.init() for migration_name in migration_db.list(): if migration_state.is_applied(migration_name, use_cache=True): print "* [+] %s" % migration_name else: print "* [ ] %s" % migration_name migrations_not_in_db = migration_state.applied_but_not_in_db() if migrations_not_in_db: print "These migrations are marked as applied but cannot " \ "be found:" for migration_name in migrations_not_in_db: print "* [?] %s" % migration_name return elif args[0] == 'init': migration_state.init() elif args[0] == 'cat': for name in args[1:]: print open( migration_db.resolve_migration_path(name), 'r' ).read() return else: raise CommandError( 'Argument should be one of: list, help, up, down, all, all_hard, init, ' 'apply, unapply, to, downto, upto, mark_as_applied, ' 'mark_as_unapplied' ) # Ensure Django permissions and content_types have been created # NOTE: Don't run if django_content_type doesn't exist yet. if table_present('django_content_type'): from django.contrib.auth.management import create_permissions from django.db import models for app in models.get_apps(): if verbosity >= 1: create_permissions(app, set(), 2) else: create_permissions(app, set(), 1)
def handle(self, *args, **options): try: migrations_dir = settings.DMIGRATIONS_DIR except AttributeError: print "You need to add DMIGRATIONS_DIR to your settings" return migration_db = MigrationDb(directory = migrations_dir) migration_state = MigrationState( migration_db = migration_db, dev = options.get('dev') ) verbosity = int(options.get('verbosity', 1)) if not args or args[0] == 'help': # TODO: Do this without calling os.system os.system('python manage.py help dmigrate') return elif args[0] in 'all up down upto downto to apply unapply'.split(): migration_state.init() for (migration_name, action) in migration_state.plan(*args): migration = migration_db.load_migration_object(migration_name) if action == 'up': if verbosity >= 1: print "Applying migration %s" % migration.name if not options.get('print_plan'): migration_state.apply(migration_name) else: if verbosity >= 1: print "Unapplying migration %s" % migration.name if not options.get('print_plan'): migration_state.unapply(migration_name) elif args[0] == 'mark_as_applied': migration_state.init() for name in args[1:]: resolved_name = migration_state.resolve_name(name) if resolved_name == None: raise NoSuchMigrationError(name) migration_state.mark_as_applied(resolved_name) elif args[0] == 'mark_as_unapplied': migration_state.init() for name in args[1:]: resolved_name = migration_state.resolve_name(name) if resolved_name == None: raise NoSuchMigrationError(name) migration_state.mark_as_unapplied(resolved_name) elif args[0] == 'list': migration_state.init() for migration_name in migration_db.list(): if migration_state.is_applied(migration_name): print "* [+] %s" % migration_name else: print "* [ ] %s" % migration_name migrations_not_in_db = migration_state.applied_but_not_in_db() if migrations_not_in_db: print "These migrations are marked as applied but cannot " \ "be found:" for migration_name in migrations_not_in_db: print "* [?] %s" % migration_name return elif args[0] == 'init': migration_state.init() elif args[0] == 'cat': for name in args[1:]: print open( migration_db.resolve_migration_path(name), 'r' ).read() return else: raise CommandError( 'Argument should be one of: list, help, up, down, all, init, ' 'apply, unapply, to, downto, upto, mark_as_applied, ' 'mark_as_unapplied' ) # Ensure Django permissions and content_types have been created # NOTE: Don't run if django_content_type doesn't exist yet. if table_present('django_content_type'): from django.contrib.auth.management import create_permissions from django.db import models for app in models.get_apps(): if verbosity >= 1: create_permissions(app, set(), 2) else: create_permissions(app, set(), 1)
def test_migration_path(self): db = MigrationDb(directory=self.mock_migrations_dir) db.warn = WarningsMocker() self.assert_equal(self.mock_migrations_dir + '/010_foo.py', db.migration_path('foo'))