def test_install_plugin(self): """ Test we can load the example plugin that every version of MySQL ships with. """ self.assertPluginNotExists("metadata_lock_info") state = ProjectState() operation = InstallPlugin("metadata_lock_info", "metadata_lock_info.so") self.assertEqual( operation.describe(), "Installs plugin metadata_lock_info from metadata_lock_info.so" ) new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("django_mysql_tests", editor, state, new_state) self.assertPluginExists("metadata_lock_info") new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("django_mysql_tests", editor, new_state, state) self.assertPluginNotExists("metadata_lock_info")
def test_install_plugin(self): """ Test we can load the example plugin that every version of MySQL ships with. """ assert not plugin_exists("metadata_lock_info") state = ProjectState() operation = InstallPlugin("metadata_lock_info", "metadata_lock_info.so") assert ( operation.describe() == "Installs plugin metadata_lock_info from metadata_lock_info.so" ) new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("testapp", editor, state, new_state) assert plugin_exists("metadata_lock_info") new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("testapp", editor, new_state, state) assert not plugin_exists("metadata_lock_info")
def test_remove_relations(self): """ #24225 - Tests that relations between models are updated while remaining the relations and references for models of an old state. """ new_apps = Apps() class A(models.Model): class Meta: app_label = "something" apps = new_apps class B(models.Model): to_a = models.ForeignKey(A, models.CASCADE) class Meta: app_label = "something" apps = new_apps def get_model_a(state): return [ mod for mod in state.apps.get_models() if mod._meta.model_name == 'a' ][0] project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = RemoveField("b", "to_a") operation.state_forwards("something", project_state) # Tests that model from old_state still has the relation model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0) # Same test for deleted model project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) old_state = project_state.clone() operation = DeleteModel("b") operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0)
def test_remove_relations(self): """ #24225 - Tests that relations between models are updated while remaining the relations and references for models of an old state. """ new_apps = Apps() class A(models.Model): class Meta: app_label = "something" apps = new_apps class B(models.Model): to_a = models.ForeignKey(A, models.CASCADE) class Meta: app_label = "something" apps = new_apps def get_model_a(state): return [mod for mod in state.apps.get_models() if mod._meta.model_name == 'a'][0] project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = RemoveField("b", "to_a") operation.state_forwards("something", project_state) # Tests that model from old_state still has the relation model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0) # Same test for deleted model project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) old_state = project_state.clone() operation = DeleteModel("b") operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0)
def test_manager_refer_correct_model_version(self): """ #24147 - Tests that managers refer to the correct version of a historical model """ project_state = ProjectState() project_state.add_model( ModelState( app_label="migrations", name="Tag", fields=[("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField())], managers=[("food_mgr", FoodManager("a", "b")), ("food_qs", FoodQuerySet.as_manager())], ) ) old_model = project_state.apps.get_model("migrations", "tag") new_state = project_state.clone() operation = RemoveField("tag", "hidden") operation.state_forwards("migrations", new_state) new_model = new_state.apps.get_model("migrations", "tag") self.assertIsNot(old_model, new_model) self.assertIs(old_model, old_model.food_mgr.model) self.assertIs(old_model, old_model.food_qs.model) self.assertIs(new_model, new_model.food_mgr.model) self.assertIs(new_model, new_model.food_qs.model) self.assertIsNot(old_model.food_mgr, new_model.food_mgr) self.assertIsNot(old_model.food_qs, new_model.food_qs) self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model) self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model)
def test_demo_schemata_get_migrated(self): user = User.objects.create_user(**CREDENTIALS) schema = DemoSchema.objects.create(user=user, from_template=self.template) operation = migrations.CreateModel("Pony", [ ('pony_id', models.AutoField(primary_key=True)), ('pink', models.IntegerField(default=1)), ]) project_state = ProjectState() new_state = project_state.clone() operation.state_forwards('tests', new_state) schema.activate() self.assertFalse('tests_pony' in get_table_list()) with connection.schema_editor() as editor: operation.database_forwards('tests', editor, project_state, new_state) schema.activate() self.assertTrue('tests_pony' in get_table_list()) with connection.schema_editor() as editor: operation.database_backwards('tests', editor, new_state, project_state) schema.activate() self.assertFalse('tests_pony' in get_table_list())
def test_create_model(self): """ Tests the CreateModel operation. Most other tests use this operation as part of setup, so check failures here first. """ operation = migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1)), ], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crmo", new_state) self.assertEqual(new_state.models["test_crmo", "pony"].name, "Pony") self.assertEqual(len(new_state.models["test_crmo", "pony"].fields), 2) # Test the database alteration self.assertTableNotExists("test_crmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crmo", editor, project_state, new_state) self.assertTableExists("test_crmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmo", editor, new_state, project_state) self.assertTableNotExists("test_crmo_pony") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "CreateModel") self.assertEqual(len(definition[1]), 2) self.assertEqual(len(definition[2]), 0) self.assertEqual(definition[1][0], "Pony")
def test_create_non_deterministic_collation(self): operation = CreateCollation( 'case_insensitive_test', 'und-u-ks-level2', provider='icu', deterministic=False, ) project_state = ProjectState() new_state = project_state.clone() # Create a collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 1) self.assertIn('CREATE COLLATION', captured_queries[0]['sql']) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertEqual(len(captured_queries), 1) self.assertIn('DROP COLLATION', captured_queries[0]['sql']) # Deconstruction. name, args, kwargs = operation.deconstruct() self.assertEqual(name, 'CreateCollation') self.assertEqual(args, []) self.assertEqual( kwargs, { 'name': 'case_insensitive_test', 'locale': 'und-u-ks-level2', 'provider': 'icu', 'deterministic': False, })
def test_add_relations(self): """ #24573 - Adding relations to existing models should reload the referenced models too. """ class A(models.Model): class Meta: app_label = 'something' class B(A): class Meta: app_label = 'something' class C(models.Model): class Meta: app_label = 'something' project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.apps # We need to work with rendered models old_state = project_state.clone() model_a_old = old_state.apps.get_model('something', 'A') model_b_old = old_state.apps.get_model('something', 'B') model_c_old = old_state.apps.get_model('something', 'C') # Check that the relations between the old models are correct self.assertIs( model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs( model_b_old._meta.get_field('a_ptr').related_model, model_a_old) operation = AddField( 'c', 'to_a', models.OneToOneField('something.A', related_name='from_c')) operation.state_forwards('something', project_state) model_a_new = project_state.apps.get_model('something', 'A') model_b_new = project_state.apps.get_model('something', 'B') model_c_new = project_state.apps.get_model('something', 'C') # Check that all models have changed self.assertIsNot(model_a_old, model_a_new) self.assertIsNot(model_b_old, model_b_new) self.assertIsNot(model_c_old, model_c_new) # Check that the relations between the old models still hold self.assertIs( model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs( model_b_old._meta.get_field('a_ptr').related_model, model_a_old) # Check that the relations between the new models correct self.assertIs( model_a_new._meta.get_field('b').related_model, model_b_new) self.assertIs( model_b_new._meta.get_field('a_ptr').related_model, model_a_new) self.assertIs( model_a_new._meta.get_field('from_c').related_model, model_c_new) self.assertIs( model_c_new._meta.get_field('to_a').related_model, model_a_new)
def test_create_model(self): """ Tests the CreateModel operation. """ operation = migrations.CreateModel( "uuidmodel", [ ("id", models.AutoField(primary_key=True)), ("uuid", UUIDField(auto=True)), ], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crmo", new_state) self.assertEqual(new_state.models["test_crmo", "uuidmodel"].name, "uuidmodel") self.assertEqual(len(new_state.models["test_crmo", "uuidmodel"].fields), 2) # Test the database alteration self.assertTableNotExists("test_crmo_uuidmodel") with connection.schema_editor() as editor: operation.database_forwards("test_crmo", editor, project_state, new_state) self.assertTableExists("test_crmo_uuidmodel") self.assertColumnExists("test_crmo_uuidmodel", "uuid") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmo", editor, new_state, project_state) self.assertTableNotExists("test_crmo_uuidmodel") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "CreateModel") self.assertEqual(len(definition[1]), 2) self.assertEqual(len(definition[2]), 0) self.assertEqual(definition[1][0], "uuidmodel")
def test_create(self): operation = CreateCollation("C_test", locale="C") self.assertEqual(operation.migration_name_fragment, "create_collation_c_test") self.assertEqual(operation.describe(), "Create collation C_test") project_state = ProjectState() new_state = project_state.clone() # Create a collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards( self.app_label, editor, project_state, new_state ) self.assertEqual(len(captured_queries), 1) self.assertIn("CREATE COLLATION", captured_queries[0]["sql"]) # Creating the same collation raises an exception. with self.assertRaisesMessage(ProgrammingError, "already exists"): with connection.schema_editor(atomic=True) as editor: operation.database_forwards( self.app_label, editor, project_state, new_state ) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards( self.app_label, editor, new_state, project_state ) self.assertEqual(len(captured_queries), 1) self.assertIn("DROP COLLATION", captured_queries[0]["sql"]) # Deconstruction. name, args, kwargs = operation.deconstruct() self.assertEqual(name, "CreateCollation") self.assertEqual(args, []) self.assertEqual(kwargs, {"name": "C_test", "locale": "C"})
def test_self_relation(self): """ #24513 - Modifying an object pointing to itself would cause it to be rendered twice and thus breaking its related M2M through objects. """ class A(models.Model): to_a = models.ManyToManyField('something.A', symmetrical=False) class Meta: app_label = "something" def get_model_a(state): return [ mod for mod in state.apps.get_models() if mod._meta.model_name == 'a' ][0] project_state = ProjectState() project_state.add_model((ModelState.from_model(A))) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = AlterField(model_name="a", name="to_a", field=models.ManyToManyField("something.A", symmetrical=False, blank=True)) # At this point the model would be rendered twice causing its related # M2M through objects to point to an old copy and thus breaking their # attribute lookup. operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) # Tests that the old model's _meta is still consistent field_to_a_old = model_a_old._meta.get_field("to_a") self.assertEqual(field_to_a_old.m2m_field_name(), "from_a") self.assertEqual(field_to_a_old.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_old.related_model, model_a_old) self.assertIs( field_to_a_old.remote_field.through._meta.get_field( 'to_a').related_model, model_a_old) self.assertIs( field_to_a_old.remote_field.through._meta.get_field( 'from_a').related_model, model_a_old) # Tests that the new model's _meta is still consistent field_to_a_new = model_a_new._meta.get_field("to_a") self.assertEqual(field_to_a_new.m2m_field_name(), "from_a") self.assertEqual(field_to_a_new.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_new.related_model, model_a_new) self.assertIs( field_to_a_new.remote_field.through._meta.get_field( 'to_a').related_model, model_a_new) self.assertIs( field_to_a_new.remote_field.through._meta.get_field( 'from_a').related_model, model_a_new)
def test_create_model(self): """ Tests that CreateModel honours multi-db settings. """ operation = migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1)), ], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crmo", new_state) # Test the database alteration self.assertTableNotExists("test_crmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crmo", editor, project_state, new_state) self.assertTableNotExists("test_crmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmo", editor, new_state, project_state) self.assertTableNotExists("test_crmo_pony")
def test_manager_refer_correct_model_version(self): """ #24147 - Tests that managers refer to the correct version of a historical model """ project_state = ProjectState() project_state.add_model( ModelState(app_label="migrations", name="Tag", fields=[ ("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField()), ], managers=[ ('food_mgr', FoodManager('a', 'b')), ('food_qs', FoodQuerySet.as_manager()), ])) old_model = project_state.apps.get_model('migrations', 'tag') new_state = project_state.clone() operation = RemoveField("tag", "hidden") operation.state_forwards("migrations", new_state) new_model = new_state.apps.get_model('migrations', 'tag') self.assertIsNot(old_model, new_model) self.assertIs(old_model, old_model.food_mgr.model) self.assertIs(old_model, old_model.food_qs.model) self.assertIs(new_model, new_model.food_mgr.model) self.assertIs(new_model, new_model.food_qs.model) self.assertIsNot(old_model.food_mgr, new_model.food_mgr) self.assertIsNot(old_model.food_qs, new_model.food_qs) self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model) self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model)
def test_create(self): operation = CreateCollation('C_test', locale='C') self.assertEqual(operation.migration_name_fragment, 'create_collation_c_test') self.assertEqual(operation.describe(), 'Create collation C_test') project_state = ProjectState() new_state = project_state.clone() # Create a collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 1) self.assertIn('CREATE COLLATION', captured_queries[0]['sql']) # Creating the same collation raises an exception. with self.assertRaisesMessage(ProgrammingError, 'already exists'): with connection.schema_editor(atomic=True) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertEqual(len(captured_queries), 1) self.assertIn('DROP COLLATION', captured_queries[0]['sql']) # Deconstruction. name, args, kwargs = operation.deconstruct() self.assertEqual(name, 'CreateCollation') self.assertEqual(args, []) self.assertEqual(kwargs, {'name': 'C_test', 'locale': 'C'})
def _test_create_model(self, app_label, should_run): """ Tests that CreateModel honours multi-db settings. """ operation = migrations.CreateModel( "Pony", [("id", models.AutoField(primary_key=True))], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards(app_label, new_state) # Test the database alteration self.assertTableNotExists("%s_pony" % app_label) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, new_state) if should_run: self.assertTableExists("%s_pony" % app_label) else: self.assertTableNotExists("%s_pony" % app_label) # And test reversal with connection.schema_editor() as editor: operation.database_backwards(app_label, editor, new_state, project_state) self.assertTableNotExists("%s_pony" % app_label)
def test_mysql_cache_migration(self): out = StringIO() call_command('mysql_cache_migration', stdout=out) output = out.getvalue() # Lint it with captured_stdout() as stderr: errors = check_code(output) self.assertEqual( errors, 0, "Encountered {} errors whilst trying to lint the mysql cache " "migration.\nMigration:\n\n{}\n\nLint errors:\n\n{}" .format(errors, output, stderr.getvalue()) ) # Dynamic import and check migration_mod = imp.new_module('0001_add_cache_tables') six.exec_(output, migration_mod.__dict__) self.assertTrue(hasattr(migration_mod, 'Migration')) migration = migration_mod.Migration self.assertTrue(hasattr(migration, 'dependencies')) self.assertTrue(hasattr(migration, 'operations')) # Since they all have the same table name, there should only be one # operation self.assertEqual(len(migration.operations), 1) # Now run the migration forwards and backwards to check it works operation = migration.operations[0] self.drop_table() self.assertTableNotExists(self.table_name) state = ProjectState() new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("django_mysql_tests", editor, state, new_state) self.assertTableExists(self.table_name) new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("django_mysql_tests", editor, new_state, state) self.assertTableNotExists(self.table_name) self.create_table()
def test_add_relations(self): """ #24573 - Adding relations to existing models should reload the referenced models too. """ new_apps = Apps() class A(models.Model): class Meta: app_label = 'something' apps = new_apps class B(A): class Meta: app_label = 'something' apps = new_apps class C(models.Model): class Meta: app_label = 'something' apps = new_apps project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.apps # We need to work with rendered models old_state = project_state.clone() model_a_old = old_state.apps.get_model('something', 'A') model_b_old = old_state.apps.get_model('something', 'B') model_c_old = old_state.apps.get_model('something', 'C') # Check that the relations between the old models are correct self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) operation = AddField('c', 'to_a', models.OneToOneField( 'something.A', models.CASCADE, related_name='from_c', )) operation.state_forwards('something', project_state) model_a_new = project_state.apps.get_model('something', 'A') model_b_new = project_state.apps.get_model('something', 'B') model_c_new = project_state.apps.get_model('something', 'C') # Check that all models have changed self.assertIsNot(model_a_old, model_a_new) self.assertIsNot(model_b_old, model_b_new) self.assertIsNot(model_c_old, model_c_new) # Check that the relations between the old models still hold self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) # Check that the relations between the new models correct self.assertIs(model_a_new._meta.get_field('b').related_model, model_b_new) self.assertIs(model_b_new._meta.get_field('a_ptr').related_model, model_a_new) self.assertIs(model_a_new._meta.get_field('from_c').related_model, model_c_new) self.assertIs(model_c_new._meta.get_field('to_a').related_model, model_a_new)
def test_install_soname(self): """ Test we can load the 'metadata_lock_info' library. """ assert not plugin_exists("metadata_lock_info") state = ProjectState() operation = InstallSOName("metadata_lock_info.so") assert operation.describe() == "Installs library metadata_lock_info.so" new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("testapp", editor, state, new_state) assert plugin_exists("metadata_lock_info") new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("testapp", editor, new_state, state) assert not plugin_exists("metadata_lock_info")
def test_create_existing_extension(self): operation = BloomExtension() project_state = ProjectState() new_state = project_state.clone() # Don't create an existing extension. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 3) self.assertIn('SELECT', captured_queries[0]['sql'])
def test_drop_nonexistent_extension(self): operation = CreateExtension('tablefunc') project_state = ProjectState() new_state = project_state.clone() # Don't drop a nonexistent extension. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 1) self.assertIn('SELECT', captured_queries[0]['sql'])
def test_mysql_cache_migration(self): out = StringIO() call_command('mysql_cache_migration', stdout=out) output = out.getvalue() # Lint it errors = flake8_code(output) assert errors == [], ( "Encountered {} errors whilst trying to lint the mysql cache " "migration.\nMigration:\n\n{}\n\nLint errors:\n\n{}" .format(len(errors), output, '\n'.join(errors)) ) # Dynamic import and check migration_mod = imp.new_module('0001_add_cache_tables') six.exec_(output, migration_mod.__dict__) assert hasattr(migration_mod, 'Migration') migration = migration_mod.Migration assert hasattr(migration, 'dependencies') assert hasattr(migration, 'operations') # Since they all have the same table name, there should only be one # operation assert len(migration.operations) == 1 # Now run the migration forwards and backwards to check it works operation = migration.operations[0] assert not self.table_exists(self.table_name) state = ProjectState() new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("testapp", editor, state, new_state) assert self.table_exists(self.table_name) new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("testapp", editor, new_state, state) assert not self.table_exists(self.table_name)
def apply_operation(operation: Operation, state: Optional[ProjectState] = None) -> ProjectState: if state is None: from_state = ProjectState() else: from_state = state.clone() to_state = from_state.clone() operation.state_forwards("tests", to_state) with connection.schema_editor() as schema_editor: operation.database_forwards("tests", schema_editor, from_state, to_state) return to_state
def test_reference_different_app_table(self): project_state = ProjectState(real_apps=['auth']) operation = migrations.Distribute('auth.User', reference=True) self.assertEqual(operation.describe(), "Run create_(distributed/reference)_table statement") self.assertTableIsNotReference('auth_user') new_state = project_state.clone() with connection.schema_editor() as editor: operation.database_forwards("tests", editor, project_state, new_state) self.assertTableIsReference('auth_user') self.undistribute_table('auth_user')
def test_mysql_cache_migration(self): out = StringIO() call_command('mysql_cache_migration', stdout=out) output = out.getvalue() # Lint it self.flake8dir.make_example_py(output) result = self.flake8dir.run_flake8() assert result.out_lines == [] # Dynamic import and check migration_mod = imp.new_module('0001_add_cache_tables') six.exec_(output, migration_mod.__dict__) assert hasattr(migration_mod, 'Migration') migration = migration_mod.Migration assert hasattr(migration, 'dependencies') assert hasattr(migration, 'operations') # Since they all have the same table name, there should only be one # operation assert len(migration.operations) == 1 # Now run the migration forwards and backwards to check it works operation = migration.operations[0] assert not self.table_exists(self.table_name) state = ProjectState() new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("testapp", editor, state, new_state) assert self.table_exists(self.table_name) new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("testapp", editor, new_state, state) assert not self.table_exists(self.table_name)
def test_distribute_table(self): project_state = ProjectState(real_apps=['tests']) operation = migrations.Distribute('MigrationTestModel') self.assertEqual(operation.describe(), "Run create_(distributed/reference)_table statement") self.assertTableIsNotDistributed('tests_migrationtestmodel', 'id') new_state = project_state.clone() with connection.schema_editor() as editor: operation.database_forwards("tests", editor, project_state, new_state) self.assertTableIsDistributed('tests_migrationtestmodel', 'id') self.undistribute_table('tests_migrationtestmodel')
def test_create_model(self): operation = migrations.CreateModel("Pony", [ ('pony_id', models.AutoField(primary_key=True)), ('pink', models.IntegerField(default=1)), ]) project_state = ProjectState() new_state = project_state.clone() operation.state_forwards('tests', new_state) self.assertTableNotExists('tests_pony') with connection.schema_editor() as editor: operation.database_forwards('tests', editor, project_state, new_state) self.assertTableExists('tests_pony') with connection.schema_editor() as editor: operation.database_backwards('tests', editor, new_state, project_state) self.assertTableNotExists('tests_pony')
def test_install_soname(self): """ Test we can load the 'metadata_lock_info' library. """ self.assertPluginNotExists("metadata_lock_info") state = ProjectState() operation = InstallSOName("metadata_lock_info.so") self.assertEqual( operation.describe(), "Installs library metadata_lock_info.so" ) new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("django_mysql_tests", editor, state, new_state) self.assertPluginExists("metadata_lock_info") new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("django_mysql_tests", editor, new_state, state) self.assertPluginNotExists("metadata_lock_info")
def test_mysql_cache_migration(self): out = StringIO() call_command("mysql_cache_migration", stdout=out) output = out.getvalue() # Lint it (self.flake8_path / "example.py").write_text(output) result = self.flake8_path.run_flake8() assert result.out_lines == [] # Dynamic import and check migration_mod = types.ModuleType("0001_add_cache_tables") exec(output, migration_mod.__dict__) assert hasattr(migration_mod, "Migration") migration = migration_mod.Migration assert hasattr(migration, "dependencies") assert hasattr(migration, "operations") # Since they all have the same table name, there should only be one # operation assert len(migration.operations) == 1 # Now run the migration forwards and backwards to check it works operation = migration.operations[0] assert not self.table_exists(self.table_name) state = ProjectState() new_state = state.clone() with connection.schema_editor() as editor: operation.database_forwards("testapp", editor, state, new_state) assert self.table_exists(self.table_name) new_state = state.clone() with connection.schema_editor() as editor: operation.database_backwards("testapp", editor, new_state, state) assert not self.table_exists(self.table_name)
def test_self_relation(self): """ #24513 - Modifying an object pointing to itself would cause it to be rendered twice and thus breaking its related M2M through objects. """ class A(models.Model): to_a = models.ManyToManyField('something.A', symmetrical=False) class Meta: app_label = "something" def get_model_a(state): return [mod for mod in state.apps.get_models() if mod._meta.model_name == 'a'][0] project_state = ProjectState() project_state.add_model((ModelState.from_model(A))) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = AlterField( model_name="a", name="to_a", field=models.ManyToManyField("something.A", symmetrical=False, blank=True) ) # At this point the model would be rendered twice causing its related # M2M through objects to point to an old copy and thus breaking their # attribute lookup. operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) # Tests that the old model's _meta is still consistent field_to_a_old = model_a_old._meta.get_field("to_a") self.assertEqual(field_to_a_old.m2m_field_name(), "from_a") self.assertEqual(field_to_a_old.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_old.related_model, model_a_old) self.assertIs(field_to_a_old.remote_field.through._meta.get_field('to_a').related_model, model_a_old) self.assertIs(field_to_a_old.remote_field.through._meta.get_field('from_a').related_model, model_a_old) # Tests that the new model's _meta is still consistent field_to_a_new = model_a_new._meta.get_field("to_a") self.assertEqual(field_to_a_new.m2m_field_name(), "from_a") self.assertEqual(field_to_a_new.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_new.related_model, model_a_new) self.assertIs(field_to_a_new.remote_field.through._meta.get_field('to_a').related_model, model_a_new) self.assertIs(field_to_a_new.remote_field.through._meta.get_field('from_a').related_model, model_a_new)
def test_no_allow_migrate(self): operation = RemoveCollation('C_test', locale='C') project_state = ProjectState() new_state = project_state.clone() # Don't create a collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 0) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertEqual(len(captured_queries), 0)
def test_allow_migrate(self): operation = CreateExtension('uuid-ossp') project_state = ProjectState() new_state = project_state.clone() # Create an extension. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertIn('CREATE EXTENSION', captured_queries[0]['sql']) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertIn('DROP EXTENSION', captured_queries[0]['sql'])
def test_collation_with_icu_provider_raises_error(self): operation = CreateCollation( 'german_phonebook', provider='icu', locale='de-u-co-phonebk', ) project_state = ProjectState() new_state = project_state.clone() msg = 'Non-libc providers require PostgreSQL 10+.' with connection.schema_editor(atomic=False) as editor: with mock.patch( 'django.db.backends.postgresql.features.DatabaseFeatures.' 'supports_alternate_collation_providers', False, ): with self.assertRaisesMessage(NotSupportedError, msg): operation.database_forwards(self.app_label, editor, project_state, new_state)
def test_equality(self): """ Tests that == and != are implemented correctly. """ # Test two things that should be equal project_state = ProjectState() project_state.add_model( ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ("hidden", models.BooleanField()), ], {}, None, ) ) other_state = project_state.clone() self.assertEqual(project_state, project_state) self.assertEqual(project_state, other_state) self.assertEqual(project_state != project_state, False) self.assertEqual(project_state != other_state, False) # Make a very small change (max_len 99) and see if that affects it project_state = ProjectState() project_state.add_model( ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=99)), ("hidden", models.BooleanField()), ], {}, None, ) ) self.assertNotEqual(project_state, other_state) self.assertEqual(project_state == other_state, False)
def test_nondeterministic_collation_not_supported(self): operation = CreateCollation( 'case_insensitive_test', provider='icu', locale='und-u-ks-level2', deterministic=False, ) project_state = ProjectState() new_state = project_state.clone() msg = 'Non-deterministic collations require PostgreSQL 12+.' with connection.schema_editor(atomic=False) as editor: with mock.patch( 'django.db.backends.postgresql.features.DatabaseFeatures.' 'supports_non_deterministic_collations', False, ): with self.assertRaisesMessage(NotSupportedError, msg): operation.database_forwards(self.app_label, editor, project_state, new_state)
def test_equality(self): """ Tests that == and != are implemented correctly. """ # Test two things that should be equal project_state = ProjectState() project_state.add_model( ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ("hidden", models.BooleanField()), ], {}, None, )) project_state.apps # Fill the apps cached property other_state = project_state.clone() self.assertEqual(project_state, project_state) self.assertEqual(project_state, other_state) self.assertEqual(project_state != project_state, False) self.assertEqual(project_state != other_state, False) self.assertNotEqual(project_state.apps, other_state.apps) # Make a very small change (max_len 99) and see if that affects it project_state = ProjectState() project_state.add_model( ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=99)), ("hidden", models.BooleanField()), ], {}, None, )) self.assertNotEqual(project_state, other_state) self.assertEqual(project_state == other_state, False)
def test_allow_migrate(self): operation = CreateExtension('tablefunc') self.assertEqual(operation.migration_name_fragment, 'create_extension_tablefunc') project_state = ProjectState() new_state = project_state.clone() # Create an extension. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 4) self.assertIn('CREATE EXTENSION', captured_queries[1]['sql']) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertEqual(len(captured_queries), 2) self.assertIn('DROP EXTENSION', captured_queries[1]['sql'])
def test_create_model(self): """ Tests that CreateModel honours multi-db settings. """ operation = migrations.CreateModel( "Pony", [("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1))] ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crmo", new_state) # Test the database alteration self.assertTableNotExists("test_crmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crmo", editor, project_state, new_state) self.assertTableNotExists("test_crmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmo", editor, new_state, project_state) self.assertTableNotExists("test_crmo_pony")
def test_migrations_apply_to_templates(self): template = SchemaTemplate.objects.create(name='a') operation = migrations.CreateModel("Pony", [ ('pony_id', models.AutoField(primary_key=True)), ('pink', models.IntegerField(default=1)), ]) project_state = ProjectState() new_state = project_state.clone() operation.state_forwards('tests', new_state) template.activate() self.assertFalse('tests_pony' in get_table_list()) with connection.schema_editor() as editor: operation.database_forwards('tests', editor, project_state, new_state) template.activate() self.assertTrue('tests_pony' in get_table_list()) with connection.schema_editor() as editor: operation.database_backwards('tests', editor, new_state, project_state) template.activate() self.assertFalse('tests_pony' in get_table_list())
def test_create_collation_alternate_provider(self): operation = CreateCollation( 'german_phonebook_test', provider='icu', locale='de-u-co-phonebk', ) project_state = ProjectState() new_state = project_state.clone() # Create an collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards(self.app_label, editor, project_state, new_state) self.assertEqual(len(captured_queries), 1) self.assertIn('CREATE COLLATION', captured_queries[0]['sql']) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards(self.app_label, editor, new_state, project_state) self.assertEqual(len(captured_queries), 1) self.assertIn('DROP COLLATION', captured_queries[0]['sql'])
def test_create_non_deterministic_collation(self): operation = CreateCollation( "case_insensitive_test", "und-u-ks-level2", provider="icu", deterministic=False, ) project_state = ProjectState() new_state = project_state.clone() # Create a collation. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_forwards( self.app_label, editor, project_state, new_state ) self.assertEqual(len(captured_queries), 1) self.assertIn("CREATE COLLATION", captured_queries[0]["sql"]) # Reversal. with CaptureQueriesContext(connection) as captured_queries: with connection.schema_editor(atomic=False) as editor: operation.database_backwards( self.app_label, editor, new_state, project_state ) self.assertEqual(len(captured_queries), 1) self.assertIn("DROP COLLATION", captured_queries[0]["sql"]) # Deconstruction. name, args, kwargs = operation.deconstruct() self.assertEqual(name, "CreateCollation") self.assertEqual(args, []) self.assertEqual( kwargs, { "name": "case_insensitive_test", "locale": "und-u-ks-level2", "provider": "icu", "deterministic": False, }, )
def test_clone_schema_with_trigger(self): TRIGGER_FUNCTION = '''CREATE OR REPLACE FUNCTION trigger_this() RETURNS TRIGGER AS $$ BEGIN RAISE EXCEPTION 'Trigger fired correctly'; END; $$ LANGUAGE plpgsql''' TRIGGER_TRIGGER = '''CREATE TRIGGER "test_trigger_this" BEFORE INSERT ON tests_awaremodel FOR EACH STATEMENT EXECUTE PROCEDURE trigger_this()''' project_state = ProjectState() new_state = project_state.clone() trigger_function_op = migrations.RunSQL( sql=TRIGGER_FUNCTION, reverse_sql='DROP FUNCTION trigger_this()' ) trigger_op = migrations.RunSQL( sql=TRIGGER_TRIGGER, reverse_sql='DROP TRIGGER "test_trigger_this" BEFORE EACH UPDATE ON tests_awaremodel' ) trigger_function_op.state_forwards('tests', new_state) trigger_op.state_forwards('tests', new_state) with connection.schema_editor() as editor: trigger_function_op.database_forwards('tests', editor, project_state, new_state) trigger_op.database_forwards('tests', editor, project_state, new_state) Schema.objects.mass_create('a', 'b', 'c') for schema in Schema.objects.all(): schema.activate() with transaction.atomic(): with self.assertRaises(Exception) as exc: self.assertTrue('Trigger fired correctly' == exc.args[0]) AwareModel.objects.create(name='FAILCREATE')
def _test_create_model(self, app_label, should_run): """ CreateModel honors multi-db settings. """ operation = migrations.CreateModel( "Pony", [("id", models.AutoField(primary_key=True))], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards(app_label, new_state) # Test the database alteration self.assertTableNotExists("%s_pony" % app_label) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, new_state) if should_run: self.assertTableExists("%s_pony" % app_label) else: self.assertTableNotExists("%s_pony" % app_label) # And test reversal with connection.schema_editor() as editor: operation.database_backwards(app_label, editor, new_state, project_state) self.assertTableNotExists("%s_pony" % app_label)