Loading django/db/backends/base/schema.py +13 −18 Original line number Diff line number Diff line Loading @@ -315,15 +315,7 @@ class BaseDatabaseSchemaEditor(object): news = set(tuple(fields) for fields in new_unique_together) # Deleted uniques for fields in olds.difference(news): columns = [model._meta.get_field(field).column for field in fields] constraint_names = self._constraint_names(model, columns, unique=True) if len(constraint_names) != 1: raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % ( len(constraint_names), model._meta.db_table, ", ".join(columns), )) self.execute(self._delete_constraint_sql(self.sql_delete_unique, model, constraint_names[0])) self._delete_composed_index(model, fields, {'unique': True}, self.sql_delete_unique) # Created uniques for fields in news.difference(olds): columns = [model._meta.get_field(field).column for field in fields] Loading @@ -339,19 +331,22 @@ class BaseDatabaseSchemaEditor(object): news = set(tuple(fields) for fields in new_index_together) # Deleted indexes for fields in olds.difference(news): self._delete_composed_index(model, fields, {'index': True}, self.sql_delete_index) # Created indexes for field_names in news.difference(olds): fields = [model._meta.get_field(field) for field in field_names] self.execute(self._create_index_sql(model, fields, suffix="_idx")) def _delete_composed_index(self, model, fields, constraint_kwargs, sql): columns = [model._meta.get_field(field).column for field in fields] constraint_names = self._constraint_names(model, list(columns), index=True) constraint_names = self._constraint_names(model, columns, **constraint_kwargs) if len(constraint_names) != 1: raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % ( len(constraint_names), model._meta.db_table, ", ".join(columns), )) self.execute(self._delete_constraint_sql(self.sql_delete_index, model, constraint_names[0])) # Created indexes for field_names in news.difference(olds): fields = [model._meta.get_field(field) for field in field_names] self.execute(self._create_index_sql(model, fields, suffix="_idx")) self.execute(self._delete_constraint_sql(sql, model, constraint_names[0])) def alter_db_table(self, model, old_db_table, new_db_table): """ Loading django/db/backends/mysql/schema.py +18 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,24 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): field.db_index = False return super(DatabaseSchemaEditor, self)._model_indexes_sql(model) def _delete_composed_index(self, model, fields, *args): """ MySQL can remove an implicit FK index on a field when that field is covered by another index like a unique_together. "covered" here means that the more complex index starts like the simpler one. http://bugs.mysql.com/bug.php?id=37910 / Django ticket #24757 We check here before removing the [unique|index]_together if we have to recreate a FK index. """ first_field = model._meta.get_field(fields[0]) if first_field.get_internal_type() == 'ForeignKey': constraint_names = self._constraint_names(model, fields[0], index=True) if not constraint_names: self.execute( self._create_index_sql(model, [model._meta.get_field(fields[0])], suffix="") ) return super(DatabaseSchemaEditor, self)._delete_composed_index(model, fields, *args) def _alter_column_type_sql(self, table, old_field, new_field, new_type): # Keep null property of old field, if it has changed, it will be handled separately if old_field.null: Loading docs/releases/1.8.2.txt +3 −0 Original line number Diff line number Diff line Loading @@ -27,3 +27,6 @@ Bugfixes :ticket:`24712`). * Fixed ``isnull`` lookup for ``HStoreField`` (:ticket:`24751`). * Fixed a MySQL crash when a migration removes a combined index (unique_together or index_together) containing a foreign key (:ticket:`24757`). tests/schema/tests.py +36 −0 Original line number Diff line number Diff line Loading @@ -1084,6 +1084,24 @@ class SchemaTests(TransactionTestCase): self.assertRaises(IntegrityError, UniqueTest.objects.create, year=2012, slug="foo") UniqueTest.objects.all().delete() def test_unique_together_with_fk(self): """ Tests removing and adding unique_together constraints that include a foreign key. """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the fields are unique to begin with self.assertEqual(Book._meta.unique_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: editor.alter_unique_together(Book, [], [['author', 'title']]) # Alter it back with connection.schema_editor() as editor: editor.alter_unique_together(Book, [['author', 'title']], []) def test_index_together(self): """ Tests removing and adding index_together constraints on a model. Loading Loading @@ -1127,6 +1145,24 @@ class SchemaTests(TransactionTestCase): ), ) def test_index_together_with_fk(self): """ Tests removing and adding index_together constraints that include a foreign key. """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the fields are unique to begin with self.assertEqual(Book._meta.index_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: editor.alter_index_together(Book, [], [['author', 'title']]) # Alter it back with connection.schema_editor() as editor: editor.alter_index_together(Book, [['author', 'title']], []) def test_create_index_together(self): """ Tests creating models with index_together already defined Loading Loading
django/db/backends/base/schema.py +13 −18 Original line number Diff line number Diff line Loading @@ -315,15 +315,7 @@ class BaseDatabaseSchemaEditor(object): news = set(tuple(fields) for fields in new_unique_together) # Deleted uniques for fields in olds.difference(news): columns = [model._meta.get_field(field).column for field in fields] constraint_names = self._constraint_names(model, columns, unique=True) if len(constraint_names) != 1: raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % ( len(constraint_names), model._meta.db_table, ", ".join(columns), )) self.execute(self._delete_constraint_sql(self.sql_delete_unique, model, constraint_names[0])) self._delete_composed_index(model, fields, {'unique': True}, self.sql_delete_unique) # Created uniques for fields in news.difference(olds): columns = [model._meta.get_field(field).column for field in fields] Loading @@ -339,19 +331,22 @@ class BaseDatabaseSchemaEditor(object): news = set(tuple(fields) for fields in new_index_together) # Deleted indexes for fields in olds.difference(news): self._delete_composed_index(model, fields, {'index': True}, self.sql_delete_index) # Created indexes for field_names in news.difference(olds): fields = [model._meta.get_field(field) for field in field_names] self.execute(self._create_index_sql(model, fields, suffix="_idx")) def _delete_composed_index(self, model, fields, constraint_kwargs, sql): columns = [model._meta.get_field(field).column for field in fields] constraint_names = self._constraint_names(model, list(columns), index=True) constraint_names = self._constraint_names(model, columns, **constraint_kwargs) if len(constraint_names) != 1: raise ValueError("Found wrong number (%s) of constraints for %s(%s)" % ( len(constraint_names), model._meta.db_table, ", ".join(columns), )) self.execute(self._delete_constraint_sql(self.sql_delete_index, model, constraint_names[0])) # Created indexes for field_names in news.difference(olds): fields = [model._meta.get_field(field) for field in field_names] self.execute(self._create_index_sql(model, fields, suffix="_idx")) self.execute(self._delete_constraint_sql(sql, model, constraint_names[0])) def alter_db_table(self, model, old_db_table, new_db_table): """ Loading
django/db/backends/mysql/schema.py +18 −0 Original line number Diff line number Diff line Loading @@ -62,6 +62,24 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): field.db_index = False return super(DatabaseSchemaEditor, self)._model_indexes_sql(model) def _delete_composed_index(self, model, fields, *args): """ MySQL can remove an implicit FK index on a field when that field is covered by another index like a unique_together. "covered" here means that the more complex index starts like the simpler one. http://bugs.mysql.com/bug.php?id=37910 / Django ticket #24757 We check here before removing the [unique|index]_together if we have to recreate a FK index. """ first_field = model._meta.get_field(fields[0]) if first_field.get_internal_type() == 'ForeignKey': constraint_names = self._constraint_names(model, fields[0], index=True) if not constraint_names: self.execute( self._create_index_sql(model, [model._meta.get_field(fields[0])], suffix="") ) return super(DatabaseSchemaEditor, self)._delete_composed_index(model, fields, *args) def _alter_column_type_sql(self, table, old_field, new_field, new_type): # Keep null property of old field, if it has changed, it will be handled separately if old_field.null: Loading
docs/releases/1.8.2.txt +3 −0 Original line number Diff line number Diff line Loading @@ -27,3 +27,6 @@ Bugfixes :ticket:`24712`). * Fixed ``isnull`` lookup for ``HStoreField`` (:ticket:`24751`). * Fixed a MySQL crash when a migration removes a combined index (unique_together or index_together) containing a foreign key (:ticket:`24757`).
tests/schema/tests.py +36 −0 Original line number Diff line number Diff line Loading @@ -1084,6 +1084,24 @@ class SchemaTests(TransactionTestCase): self.assertRaises(IntegrityError, UniqueTest.objects.create, year=2012, slug="foo") UniqueTest.objects.all().delete() def test_unique_together_with_fk(self): """ Tests removing and adding unique_together constraints that include a foreign key. """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the fields are unique to begin with self.assertEqual(Book._meta.unique_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: editor.alter_unique_together(Book, [], [['author', 'title']]) # Alter it back with connection.schema_editor() as editor: editor.alter_unique_together(Book, [['author', 'title']], []) def test_index_together(self): """ Tests removing and adding index_together constraints on a model. Loading Loading @@ -1127,6 +1145,24 @@ class SchemaTests(TransactionTestCase): ), ) def test_index_together_with_fk(self): """ Tests removing and adding index_together constraints that include a foreign key. """ # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) # Ensure the fields are unique to begin with self.assertEqual(Book._meta.index_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: editor.alter_index_together(Book, [], [['author', 'title']]) # Alter it back with connection.schema_editor() as editor: editor.alter_index_together(Book, [['author', 'title']], []) def test_create_index_together(self): """ Tests creating models with index_together already defined Loading