def test_bool(self): """Testing MigrationList.__bool__""" migration_list = MigrationList() self.assertFalse(migration_list) migration_list.add_migration_info(app_label='tests', name='0001_initial') self.assertTrue(migration_list)
def test_add_recorded_migration(self): """Testing MigrationList.add_recorded_migration""" recorded_migration1 = MigrationRecorder.Migration( app='tests', name='0001_initial', applied=True) recorded_migration2 = MigrationRecorder.Migration( app='tests', name='0002_add_field', applied=True) migration_list = MigrationList() migration_list.add_recorded_migration(recorded_migration1) migration_list.add_recorded_migration(recorded_migration2) self.assertEqual( migration_list._by_id, { ('tests', '0001_initial'): { 'app_label': 'tests', 'name': '0001_initial', 'migration': None, 'recorded_migration': recorded_migration1, }, ('tests', '0002_add_field'): { 'app_label': 'tests', 'name': '0002_add_field', 'migration': None, 'recorded_migration': recorded_migration2, }, }) self.assertEqual( migration_list._by_app_label, { 'tests': [ { 'app_label': 'tests', 'name': '0001_initial', 'migration': None, 'recorded_migration': recorded_migration1, }, { 'app_label': 'tests', 'name': '0002_add_field', 'migration': None, 'recorded_migration': recorded_migration2, }, ], })
def test_get_app_labels(self): """Testing MigrationList.get_app_labels""" migration_list = MigrationList() migration_list.add_migration_info(app_label='foo', name='0002_bar') migration_list.add_migration_info(app_label='tests', name='0001_initial') migration_list.add_migration_info(app_label='baz', name='0002_stuff') self.assertEqual(migration_list.get_app_labels(), ['baz', 'foo', 'tests'])
def test_from_names(self): """Testing MigrationList.from_names""" migration_list = MigrationList.from_names( app_label='tests', migration_names=['0001_initial', '0002_stuff']) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0001_initial')) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0002_stuff'))
def test_add_migration(self): """Testing MigrationList.add_migration""" migration1 = InitialMigration('0001_initial', 'tests') migration2 = AddFieldMigration('0002_add_field', 'tests') migration_list = MigrationList() migration_list.add_migration(migration1) migration_list.add_migration(migration2) self.assertEqual( migration_list._by_id, { ('tests', '0001_initial'): { 'app_label': 'tests', 'name': '0001_initial', 'migration': migration1, 'recorded_migration': None, }, ('tests', '0002_add_field'): { 'app_label': 'tests', 'name': '0002_add_field', 'migration': migration2, 'recorded_migration': None, }, }) self.assertEqual( migration_list._by_app_label, { 'tests': [ { 'app_label': 'tests', 'name': '0001_initial', 'migration': migration1, 'recorded_migration': None, }, { 'app_label': 'tests', 'name': '0002_add_field', 'migration': migration2, 'recorded_migration': None, }, ], })
def test_add_migration_targets(self): """Testing MigrationList.add_migration_targets""" migration_list = MigrationList() migration_list.add_migration_targets([ ('tests', '0001_initial'), ('tests', '0002_stuff'), ]) self.assertEqual( migration_list._by_id, { ('tests', '0001_initial'): { 'app_label': 'tests', 'name': '0001_initial', 'migration': None, 'recorded_migration': None, }, ('tests', '0002_stuff'): { 'app_label': 'tests', 'name': '0002_stuff', 'migration': None, 'recorded_migration': None, }, }) self.assertEqual( migration_list._by_app_label, { 'tests': [ { 'app_label': 'tests', 'name': '0001_initial', 'migration': None, 'recorded_migration': None, }, { 'app_label': 'tests', 'name': '0002_stuff', 'migration': None, 'recorded_migration': None, }, ], })
def test_from_app_sig(self): """Testing MigrationList.from_app_sig""" app_sig = AppSignature( app_id='tests', applied_migrations=['0001_initial', '0002_stuff']) migration_list = MigrationList.from_app_sig(app_sig) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0001_initial')) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0002_stuff'))
def test_run_checks_with_bad_history(self): """Testing MigrationExecutor.run_checks with bad history""" connection = connections[DEFAULT_DB_ALIAS] applied_migrations = MigrationList() applied_migrations.add_migration_info(app_label='tests', name='0002_add_field') record_applied_migrations(connection=connection, migrations=applied_migrations) custom_migrations = MigrationList() custom_migrations.add_migration( InitialMigration('0001_initial', 'tests')) custom_migrations.add_migration( AddFieldMigration('0002_add_field', 'tests')) executor = MigrationExecutor(connection=connection, custom_migrations=custom_migrations) with self.assertRaises(MigrationHistoryError): executor.run_checks()
def test_eq(self): """Testing MigrationList.__eq__""" migration_list1 = MigrationList() migration_list2 = MigrationList() self.assertEqual(migration_list1, migration_list2) self.assertNotEqual(migration_list1, None) self.assertNotEqual(migration_list1, ['abc']) self.assertNotEqual(migration_list1, 123) migration_list1.add_migration_info(app_label='tests', name='0001_initial') self.assertNotEqual(migration_list1, migration_list2) migration_list2.add_migration_info(app_label='tests', name='0001_initial') self.assertEqual(migration_list1, migration_list2) migration_list2.add_migration_info(app_label='foo', name='0001_bar') self.assertNotEqual(migration_list1, migration_list2)
def test_to_targets(self): """Testing MigrationList.to_targets""" migration_list = MigrationList() migration_list.add_migration_info(app_label='tests', name='0001_initial') migration_list.add_migration_info(app_label='foo', name='0002_bar') self.assertEqual( migration_list.to_targets(), {('tests', '0001_initial'), ('foo', '0002_bar')})
def test_apply_migrations(self): """Testing apply_migrations""" database_state = DatabaseState(db_name=DEFAULT_DB_ALIAS) register_models(database_state=database_state, models=[('MigrationTestModel', MigrationTestModel)]) app_migrations = [ InitialMigration('0001_initial', 'tests'), AddFieldMigration('0002_add_field', 'tests'), ] targets = [ ('tests', '0001_initial'), ('tests', '0002_add_field'), ] custom_migrations = MigrationList() custom_migrations.add_migration(app_migrations[0]) custom_migrations.add_migration(app_migrations[1]) connection = connections[DEFAULT_DB_ALIAS] executor = MigrationExecutor(connection, custom_migrations=custom_migrations) migrate_state = apply_migrations( executor=executor, targets=targets, plan=[ (app_migrations[0], False), (app_migrations[1], False), ], pre_migrate_state=create_pre_migrate_state(executor)) finalize_migrations(migrate_state) # Make sure this is in the database now. MigrationTestModel.objects.create(field1=123, field2='abc', field3=True)
def record_applied_migrations(self, migration_targets, database=DEFAULT_DB_ALIAS): """Record applied migrations in the database. This is a convenience around creating a migration list and then recording it in the database. Args: migration_targets (list of tuple): The list of migration targets to store. Each item is a tuple in ``(app_label, label)`` form. database (unicode, optional): The name of the database to save these on. """ assert supports_migrations migration_list = MigrationList() migration_list.add_migration_targets(migration_targets) record_applied_migrations(connection=connections[database], migrations=migration_list)
def test_has_migration_info(self): """Testing MigrationList.has_migration_info""" migration_list = MigrationList() migration_list.add_migration_info(app_label='tests', name='0001_initial') self.assertTrue(migration_list.has_migration_info( app_label='tests', name='0001_initial')) self.assertFalse(migration_list.has_migration_info( app_label='tests', name='0002_initial')) self.assertFalse(migration_list.has_migration_info( app_label='foo', name='0001_initial'))
def test_len(self): """Testing MigrationList.__len__""" migration_list = MigrationList() self.assertEqual(len(migration_list), 0) migration_list.add_migration_info(app_label='tests', name='0001_initial') self.assertEqual(len(migration_list), 1) migration_list.add_migration_info(app_label='tests', name='0002_stuff') self.assertEqual(len(migration_list), 2)
def test_clone(self): """Testing MigrationList.clone""" migration_list = MigrationList() migration_list.add_migration_info(app_label='tests', name='0001_initial') migration_list.add_migration_info(app_label='foo', name='0002_bar') cloned = migration_list.clone() self.assertIsNot(migration_list._by_id, cloned._by_id) self.assertIsNot(migration_list._by_app_label, cloned._by_app_label) self.assertEqual(migration_list._by_id, cloned._by_id) self.assertEqual(migration_list._by_app_label, cloned._by_app_label) # Change something in the original and make sure the clone isn't # affected. migration_list._by_id[('tests', '0001_initial')]['name'] = 'changed' self.assertNotEqual(migration_list._by_id, cloned._by_id) self.assertNotEqual(migration_list._by_app_label, cloned._by_app_label)
def test_from_database(self): """Testing MigrationList.from_database""" connection = connections[DEFAULT_DB_ALIAS] applied_migrations = MigrationList() applied_migrations.add_migration_info(app_label='tests', name='0001_initial') applied_migrations.add_migration_info(app_label='tests', name='0002_stuff') record_applied_migrations(connection=connection, migrations=applied_migrations) migration_list = MigrationList.from_database(connection) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0001_initial')) self.assertTrue(migration_list.has_migration_info(app_label='tests', name='0002_stuff'))
def test_record_applied_migrations(self): """Testing record_applied_migrations""" connection = connections[DEFAULT_DB_ALIAS] # Ideally we'd do an assertNumQueries(2), but MigrationRecorder doesn't # cache state and performs repeated queries for the same list of # installed table names, followed by new transactions. That might # differ depending on the type of database being used. migrations = MigrationList() migrations.add_migration_info(app_label='tests', name='0001_initial') migrations.add_migration_info(app_label='tests', name='0002_stuff') record_applied_migrations(connection=connection, migrations=migrations) recorder = MigrationRecorder(connection) applied_migrations = recorder.applied_migrations() self.assertIn(('tests', '0001_initial'), applied_migrations) self.assertIn(('tests', '0002_stuff'), applied_migrations)
def test_unrecord_applied_migrations(self): """Testing unrecord_applied_migrations""" connection = connections[DEFAULT_DB_ALIAS] migrations = MigrationList() migrations.add_migration_info(app_label='tests', name='0001_initial') migrations.add_migration_info(app_label='tests', name='0002_stuff') record_applied_migrations(connection=connection, migrations=migrations) unrecord_applied_migrations(connection=connection, app_label='tests', migration_names=['0001_initial', '0002_stuff']) recorder = MigrationRecorder(connection) applied_migrations = recorder.applied_migrations() self.assertNotIn(('tests', '0001_initial'), applied_migrations) self.assertNotIn(('tests', '0002_stuff'), applied_migrations)
def assertAppliedMigrations(self, expected_migration_targets, database=DEFAULT_DB_ALIAS): """Assert that applied migrations match expectations. Args: expected_migration_targets (list of tuple): A list of migration targets to compare against the applied migrations. Each item is a tuple in ``(app_label, name)`` form. database (unicode, optional): The name of the database to query appliedm migrations against. Raises: AssertionError: The lists do not match. """ applied_migrations = MigrationList.from_database(connections[database]) for app_label, name in expected_migration_targets: self.assertTrue( applied_migrations.has_migration_info(app_label=app_label, name=name))
def test_run_checks_with_conflicts(self): """Testing MigrationExecutor.run_checks with conflicts""" connection = connections[DEFAULT_DB_ALIAS] custom_migrations = MigrationList() custom_migrations.add_migration( InitialMigration('0001_initial', 'tests')) custom_migrations.add_migration( InitialMigration('0002_also_initial', 'tests')) executor = MigrationExecutor(connection=connection, custom_migrations=custom_migrations) message = ( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (0001_initial, 0002_also_initial in tests).\n" "To fix them run 'python manage.py makemigrations --merge'" ) with self.assertRaisesMessage(MigrationConflictsError, message): executor.run_checks()
def test_iter(self): """Testing MigrationList.__iter__""" migration_list = MigrationList() migration_list.add_migration_info(app_label='tests', name='0001_initial') migration_list.add_migration_info(app_label='tests', name='0002_stuff') self.assertEqual( list(migration_list), [ { 'app_label': 'tests', 'name': '0001_initial', 'migration': None, 'recorded_migration': None, }, { 'app_label': 'tests', 'name': '0002_stuff', 'migration': None, 'recorded_migration': None, }, ])
def test_update(self): """Testing MigrationList.update""" if supports_migrations: migration1 = InitialMigration('0001_initial', 'tests') migration2 = AddFieldMigration('0002_add_field', 'tests') recorded_migration1 = MigrationRecorder.Migration( app='tests', name='0001_initial', applied=True) recorded_migration2 = MigrationRecorder.Migration( app='tests', name='0002_add_field', applied=True) else: migration1 = None migration2 = None recorded_migration1 = None recorded_migration2 = None migration_list1 = MigrationList() migration_list1.add_migration_info( app_label='tests', name='0001_initial', migration=migration1, recorded_migration=recorded_migration1) migration_list1.add_migration_info( app_label='tests', name='0002_add_field', migration=None, recorded_migration=None) migration_list2 = MigrationList() migration_list2.add_migration_info( app_label='tests', name='0002_add_field', migration=migration2, recorded_migration=recorded_migration2) migration_list2.add_migration_info( app_label='foo', name='0001_initial') migration_list1.update(migration_list2) self.assertEqual( migration_list1._by_id, { ('tests', '0001_initial'): { 'app_label': 'tests', 'name': '0001_initial', 'migration': migration1, 'recorded_migration': recorded_migration1, }, ('tests', '0002_add_field'): { 'app_label': 'tests', 'name': '0002_add_field', 'migration': migration2, 'recorded_migration': recorded_migration2, }, ('foo', '0001_initial'): { 'app_label': 'foo', 'name': '0001_initial', 'migration': None, 'recorded_migration': None, }, }) self.assertEqual( migration_list1._by_app_label, { 'foo': [ { 'app_label': 'foo', 'name': '0001_initial', 'migration': None, 'recorded_migration': None, }, ], 'tests': [ { 'app_label': 'tests', 'name': '0001_initial', 'migration': migration1, 'recorded_migration': recorded_migration1, }, { 'app_label': 'tests', 'name': '0002_add_field', 'migration': migration2, 'recorded_migration': recorded_migration2, }, ], })
def _add_migrations(self, graph, migrations_info, leaf_migration_targets, mark_applied=[]): """Add migrations to a graph. This is a utility for simplifying the additions of a list of migrations to a graph, handling the creation of the Django migration objects, the formulation of a migration plan, and the recording of applied migrations. Args: graph (django_evolution.utils.graph.EvolutionGraph): The graph to add migrations to. migrations_info (list of tuple): The list of info on migrations to add. Each tuple contains: 1. The app label 2. The migration name 3. The migration class leaf_migration_targets (list of tuple): The list of final migration targets to migrate to. mark_applied (list of tuple, optional): The list of migration targets to mark as applied. Returns: list of tuple: The migration plan generated from the migrations. """ migration_list = MigrationList() for app_label, name, migration_cls in migrations_info: migration_list.add_migration_info( app_label=app_label, name=name, migration=migration_cls(name, app_label)) connection = connections[DEFAULT_DB_ALIAS] if mark_applied: mark_applied_list = MigrationList() mark_applied_list.add_migration_targets(mark_applied) record_applied_migrations(connection, mark_applied_list) else: mark_applied_list = None migration_executor = MigrationExecutor( connection=connection, custom_migrations=migration_list) migration_loader = MigrationLoader( connection=connection, custom_migrations=migration_list) migration_plan = \ migration_executor.migration_plan(leaf_migration_targets) migration_loader.build_graph() graph.add_migration_plan(migration_plan=migration_plan, migration_graph=migration_loader.graph) if mark_applied_list: graph.mark_migrations_applied(mark_applied_list) return migration_plan
def get_app_upgrade_info(app, scan_evolutions=True, simulate_applied=False, database=None): """Return the upgrade information to use for a given app. This will determine if the app should be using Django Evolution or Django Migrations for any schema upgrades. If an ``evolutions`` module is found, then this will determine the method to be :py:attr:`UpgradeMethod.EVOLUTIONS <django_evolution.consts.UpgradeMethod.EVOLUTIONS>`, unless the app has been moved over to using Migrations. If instead there's a ``migrations`` module, then this will determine the method to be :py:attr:`UpgradeMethod.MIGRATIONS <django_evolution.consts.UpgradeMethod.MIGRATIONS>`. Otherwise, this will return ``None``, indicating that no established method has been chosen. This allows a determination to be made later, based on the Django version or the consumer's choice. Note that this may return that migrations are the preferred method for an app even on versions of Django that do not support migrations. It's up to the caller to handle this however it chooses. Args: app (module): The app module to determine the upgrade method for. scan_evolutions (bool, optional): Whether to scan evolutions for the app to determine the current upgrade method. simulate_applied (bool, optional): Return the upgrade method based on the state of the app if all mutations had been applied. This is useful for generating end state signatures. This is ignored if passing ``scan_evolutions=False``. database (unicode, optional): The database to use for accessing stored evolution and migration information. Returns: dict: A dictionary of information containing the following keys: ``applied_migrations`` (:py:class:`~django_evolution.utils.migrations.MigrationList`): A list of migrations that have been applied to this app through any mutations. This will only be present if the upgrade method is set to use migrations and if running on a version of Django that supports migrations. ``has_evolutions`` (:py:class:`bool`): Whether there are any evolutions for this app. This may come from the app, project, or Django Evolution. ``has_migrations`` (:py:class:`bool`): Whether there are any migrations for this app. ``upgrade_method`` (:py:class:`unicode`): The upgrade method. This will be a value from :py:class:`~django_evolution.consts.UpgradeMethod`, or ``None`` if a clear determination could not be made. """ # Avoids a nasty circular import. Util modules should always be # importable, so we compensate here. from django_evolution.mutations import MoveToDjangoMigrations upgrade_method = None applied_migrations = None has_evolutions = has_evolutions_module(app) has_migrations = has_migrations_module(app) if has_evolutions: # This app made use of Django Evolution. See if we're still using # that, or if it's handed control over to Django migrations. if scan_evolutions: if simulate_applied: evolutions = None else: evolutions = get_applied_evolutions(app, database=database) mutations = get_app_mutations(app=app, evolution_labels=evolutions) for mutation in reversed(mutations): if isinstance(mutation, MoveToDjangoMigrations): app_label = get_app_label(app) upgrade_method = UpgradeMethod.MIGRATIONS applied_migrations = MigrationList() for name in mutation.mark_applied: applied_migrations.add_migration_info( app_label=app_label, name=name) break if not upgrade_method: upgrade_method = UpgradeMethod.EVOLUTIONS if has_migrations: if not upgrade_method: upgrade_method = UpgradeMethod.MIGRATIONS if supports_migrations: applied_migrations = MigrationList.from_database( connection=connections[database or DEFAULT_DB_ALIAS], app_label=get_app_label(app)) if not applied_migrations: applied_migrations = None return { 'applied_migrations': applied_migrations, 'has_evolutions': has_evolutions, 'has_migrations': has_migrations, 'upgrade_method': upgrade_method, }
def test_sub(self): """Testing MigrationList.__sub__""" migration_list1 = MigrationList() migration_list1.add_migration_info(app_label='tests', name='0001_initial') migration_list1.add_migration_info(app_label='tests', name='0002_stuff') migration_list2 = MigrationList() migration_list2.add_migration_info(app_label='tests', name='0001_initial') migration_list2.add_migration_info(app_label='foo', name='0002_bar') new_migration_list = migration_list1 - migration_list2 self.assertEqual( new_migration_list._by_id, { ('tests', '0002_stuff'): { 'app_label': 'tests', 'name': '0002_stuff', 'migration': None, 'recorded_migration': None, }, }) self.assertEqual( new_migration_list._by_app_label, { 'tests': [ { 'app_label': 'tests', 'name': '0002_stuff', 'migration': None, 'recorded_migration': None, }, ], }) # Make sure there's no cross-contamination. self.assertNotEqual(new_migration_list._by_id, migration_list1._by_id) self.assertNotEqual(new_migration_list._by_id, migration_list2._by_id) self.assertNotEqual(migration_list1._by_id, migration_list2._by_id)