Browse Source

Fixed #26171 -- Made MySQL create an index on ForeignKeys with db_contraint=False.

Refactored "Prevented unneeded index creation on MySQL-InnoDB" (2ceb10f)
to avoid setting db_index=False.
Aaron Elliot Ross 8 years ago
parent
commit
6bf7964023

+ 4 - 1
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),

+ 11 - 8
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):
         """

+ 1 - 0
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()
 

+ 5 - 2
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`)'
+        ])