diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index b6510ca626..522598d642 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -884,7 +884,7 @@ class BaseDatabaseSchemaEditor(object): return [] output = [] for field in model._meta.local_fields: - if field.db_index and not field.unique: + if self._field_should_be_indexed(model, field): output.append(self._create_index_sql(model, [field], suffix="")) for field_names in model._meta.index_together: @@ -892,6 +892,9 @@ class BaseDatabaseSchemaEditor(object): output.append(self._create_index_sql(model, fields, suffix="_idx")) return output + def _field_should_be_indexed(self, model, field): + return field.db_index and not field.unique + def _rename_field_sql(self, table, old_field, new_field, new_type): return self.sql_rename_column % { "table": self.quote_name(table), diff --git a/django/db/backends/mysql/schema.py b/django/db/backends/mysql/schema.py index 802ce6353b..4aa40f7cb5 100644 --- a/django/db/backends/mysql/schema.py +++ b/django/db/backends/mysql/schema.py @@ -51,17 +51,20 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): 'column': self.quote_name(field.column), }, [effective_default]) - def _model_indexes_sql(self, model): + def _field_should_be_indexed(self, model, field): + create_index = super(DatabaseSchemaEditor, self)._field_should_be_indexed(model, field) storage = self.connection.introspection.get_storage_engine( self.connection.cursor(), model._meta.db_table ) - if storage == "InnoDB": - for field in model._meta.local_fields: - if field.db_index and not field.unique and field.get_internal_type() == "ForeignKey": - # Temporary setting db_index to False (in memory) to disable - # index creation for FKs (index automatically created by MySQL) - field.db_index = False - return super(DatabaseSchemaEditor, self)._model_indexes_sql(model) + # No need to create an index for ForeignKey fields except if + # db_constraint=False because the index from that constraint won't be + # created. + if (storage == "InnoDB" and + create_index and + field.get_internal_type() == 'ForeignKey' and + field.db_constraint): + return False + return create_index def _delete_composed_index(self, model, fields, *args): """ diff --git a/tests/indexes/models.py b/tests/indexes/models.py index b348f0365c..dc384ab489 100644 --- a/tests/indexes/models.py +++ b/tests/indexes/models.py @@ -19,6 +19,7 @@ class CurrentTranslation(models.ForeignObject): class ArticleTranslation(models.Model): article = models.ForeignKey('indexes.Article', models.CASCADE) + article_no_constraint = models.ForeignKey('indexes.Article', models.CASCADE, db_constraint=False) language = models.CharField(max_length=10, unique=True) content = models.TextField() diff --git a/tests/indexes/tests.py b/tests/indexes/tests.py index 6e27130f63..dfc503e15d 100644 --- a/tests/indexes/tests.py +++ b/tests/indexes/tests.py @@ -62,7 +62,7 @@ class SchemaIndexesTests(TestCase): def test_no_index_for_foreignkey(self): """ MySQL on InnoDB already creates indexes automatically for foreign keys. - (#14180). + (#14180). An index should be created if db_constraint=False (#26171). """ storage = connection.introspection.get_storage_engine( connection.cursor(), ArticleTranslation._meta.db_table @@ -70,4 +70,7 @@ class SchemaIndexesTests(TestCase): if storage != "InnoDB": self.skip("This test only applies to the InnoDB storage engine") index_sql = connection.schema_editor()._model_indexes_sql(ArticleTranslation) - self.assertEqual(index_sql, []) + self.assertEqual(index_sql, [ + 'CREATE INDEX `indexes_articletranslation_99fb53c2` ' + 'ON `indexes_articletranslation` (`article_no_constraint_id`)' + ])