Loading django/db/migrations/operations/__init__.py +1 −1 Original line number Diff line number Diff line from .models import CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether from .models import CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether from .fields import AddField, RemoveField, AlterField, RenameField django/db/migrations/operations/models.py +31 −1 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ class AlterModelTable(Operation): class AlterUniqueTogether(Operation): """ Changes the value of unique_together to the target one. Changes the value of index_together to the target one. Input value of unique_together must be a set of tuples. """ Loading @@ -108,3 +108,33 @@ class AlterUniqueTogether(Operation): def describe(self): return "Alter unique_together for %s (%s constraints)" % (self.name, len(self.unique_together)) class AlterIndexTogether(Operation): """ Changes the value of index_together to the target one. Input value of index_together must be a set of tuples. """ def __init__(self, name, index_together): self.name = name.lower() self.index_together = set(tuple(cons) for cons in index_together) def state_forwards(self, app_label, state): model_state = state.models[app_label, self.name.lower()] model_state.options["index_together"] = self.index_together def database_forwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() schema_editor.alter_index_together( new_app_cache.get_model(app_label, self.name), getattr(old_app_cache.get_model(app_label, self.name)._meta, "index_together", set()), getattr(new_app_cache.get_model(app_label, self.name)._meta, "index_together", set()), ) def database_backwards(self, app_label, schema_editor, from_state, to_state): return self.database_forwards(app_label, schema_editor, from_state, to_state) def describe(self): return "Alter index_together for %s (%s constraints)" % (self.name, len(self.index_together)) tests/migrations/test_operations.py +35 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ class OperationTests(TestCase): def assertColumnNotNull(self, table, column): self.assertEqual([c.null_ok for c in connection.introspection.get_table_description(connection.cursor(), table) if c.name == column][0], False) def assertIndexExists(self, table, columns, value=True): self.assertEqual( value, any( c["index"] for c in connection.introspection.get_constraints(connection.cursor(), table).values() if c['columns'] == list(columns) ), ) def assertIndexNotExists(self, table, columns): return self.assertIndexExists(table, columns, False) def set_up_test_model(self, app_label): """ Creates a test model state and database table. Loading Loading @@ -242,3 +255,25 @@ class OperationTests(TestCase): cursor.execute("INSERT INTO test_alunto_pony (id, pink, weight) VALUES (1, 1, 1)") cursor.execute("INSERT INTO test_alunto_pony (id, pink, weight) VALUES (2, 1, 1)") cursor.execute("DELETE FROM test_alunto_pony") def test_alter_index_together(self): """ Tests the AlterIndexTogether operation. """ project_state = self.set_up_test_model("test_alinto") # Test the state alteration operation = migrations.AlterIndexTogether("Pony", [("pink", "weight")]) new_state = project_state.clone() operation.state_forwards("test_alinto", new_state) self.assertEqual(len(project_state.models["test_alinto", "pony"].options.get("index_together", set())), 0) self.assertEqual(len(new_state.models["test_alinto", "pony"].options.get("index_together", set())), 1) # Make sure there's no matching index self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"]) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alinto", editor, project_state, new_state) self.assertIndexExists("test_alinto_pony", ["pink", "weight"]) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alinto", editor, new_state, project_state) self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"]) Loading
django/db/migrations/operations/__init__.py +1 −1 Original line number Diff line number Diff line from .models import CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether from .models import CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether from .fields import AddField, RemoveField, AlterField, RenameField
django/db/migrations/operations/models.py +31 −1 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ class AlterModelTable(Operation): class AlterUniqueTogether(Operation): """ Changes the value of unique_together to the target one. Changes the value of index_together to the target one. Input value of unique_together must be a set of tuples. """ Loading @@ -108,3 +108,33 @@ class AlterUniqueTogether(Operation): def describe(self): return "Alter unique_together for %s (%s constraints)" % (self.name, len(self.unique_together)) class AlterIndexTogether(Operation): """ Changes the value of index_together to the target one. Input value of index_together must be a set of tuples. """ def __init__(self, name, index_together): self.name = name.lower() self.index_together = set(tuple(cons) for cons in index_together) def state_forwards(self, app_label, state): model_state = state.models[app_label, self.name.lower()] model_state.options["index_together"] = self.index_together def database_forwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() schema_editor.alter_index_together( new_app_cache.get_model(app_label, self.name), getattr(old_app_cache.get_model(app_label, self.name)._meta, "index_together", set()), getattr(new_app_cache.get_model(app_label, self.name)._meta, "index_together", set()), ) def database_backwards(self, app_label, schema_editor, from_state, to_state): return self.database_forwards(app_label, schema_editor, from_state, to_state) def describe(self): return "Alter index_together for %s (%s constraints)" % (self.name, len(self.index_together))
tests/migrations/test_operations.py +35 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,19 @@ class OperationTests(TestCase): def assertColumnNotNull(self, table, column): self.assertEqual([c.null_ok for c in connection.introspection.get_table_description(connection.cursor(), table) if c.name == column][0], False) def assertIndexExists(self, table, columns, value=True): self.assertEqual( value, any( c["index"] for c in connection.introspection.get_constraints(connection.cursor(), table).values() if c['columns'] == list(columns) ), ) def assertIndexNotExists(self, table, columns): return self.assertIndexExists(table, columns, False) def set_up_test_model(self, app_label): """ Creates a test model state and database table. Loading Loading @@ -242,3 +255,25 @@ class OperationTests(TestCase): cursor.execute("INSERT INTO test_alunto_pony (id, pink, weight) VALUES (1, 1, 1)") cursor.execute("INSERT INTO test_alunto_pony (id, pink, weight) VALUES (2, 1, 1)") cursor.execute("DELETE FROM test_alunto_pony") def test_alter_index_together(self): """ Tests the AlterIndexTogether operation. """ project_state = self.set_up_test_model("test_alinto") # Test the state alteration operation = migrations.AlterIndexTogether("Pony", [("pink", "weight")]) new_state = project_state.clone() operation.state_forwards("test_alinto", new_state) self.assertEqual(len(project_state.models["test_alinto", "pony"].options.get("index_together", set())), 0) self.assertEqual(len(new_state.models["test_alinto", "pony"].options.get("index_together", set())), 1) # Make sure there's no matching index self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"]) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alinto", editor, project_state, new_state) self.assertIndexExists("test_alinto_pony", ["pink", "weight"]) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alinto", editor, new_state, project_state) self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"])