Browse Source

Refs #27236 -- Removed Meta.index_together per deprecation timeline.

Mariusz Felisiak 1 year ago
parent
commit
2abf417c81

+ 3 - 9
django/db/backends/base/schema.py

@@ -501,8 +501,7 @@ class BaseDatabaseSchemaEditor:
                                 model, field, field_type, field.db_comment
                             )
                         )
-        # Add any field index and index_together's (deferred as SQLite
-        # _remake_table needs it).
+        # Add any field index (deferred as SQLite _remake_table needs it).
         self.deferred_sql.extend(self._model_indexes_sql(model))
 
         # Make M2M tables
@@ -1585,8 +1584,8 @@ class BaseDatabaseSchemaEditor:
 
     def _model_indexes_sql(self, model):
         """
-        Return a list of all index SQL statements (field indexes,
-        index_together, Meta.indexes) for the specified model.
+        Return a list of all index SQL statements (field indexes, Meta.indexes)
+        for the specified model.
         """
         if not model._meta.managed or model._meta.proxy or model._meta.swapped:
             return []
@@ -1594,11 +1593,6 @@ class BaseDatabaseSchemaEditor:
         for field in model._meta.local_fields:
             output.extend(self._field_indexes_sql(model, field))
 
-        # RemovedInDjango51Warning.
-        for field_names in model._meta.index_together:
-            fields = [model._meta.get_field(field) for field in field_names]
-            output.append(self._create_index_sql(model, fields=fields, suffix="_idx"))
-
         for index in model._meta.indexes:
             if (
                 not index.contains_expressions

+ 0 - 10
django/db/backends/sqlite3/schema.py

@@ -180,14 +180,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             for unique in model._meta.unique_together
         ]
 
-        # RemovedInDjango51Warning.
-        # Work out the new value for index_together, taking renames into
-        # account
-        index_together = [
-            [rename_mapping.get(n, n) for n in index]
-            for index in model._meta.index_together
-        ]
-
         indexes = model._meta.indexes
         if delete_field:
             indexes = [
@@ -210,7 +202,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             "app_label": model._meta.app_label,
             "db_table": model._meta.db_table,
             "unique_together": unique_together,
-            "index_together": index_together,  # RemovedInDjango51Warning.
             "indexes": indexes,
             "constraints": constraints,
             "apps": apps,
@@ -226,7 +217,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
             "app_label": model._meta.app_label,
             "db_table": "new__%s" % strip_quotes(model._meta.db_table),
             "unique_together": unique_together,
-            "index_together": index_together,  # RemovedInDjango51Warning.
             "indexes": indexes,
             "constraints": constraints,
             "apps": apps,

+ 2 - 35
django/db/migrations/autodetector.py

@@ -190,14 +190,12 @@ class MigrationAutodetector:
         self.generate_renamed_indexes()
         # Generate removal of foo together.
         self.generate_removed_altered_unique_together()
-        self.generate_removed_altered_index_together()  # RemovedInDjango51Warning.
         # Generate field operations.
         self.generate_removed_fields()
         self.generate_added_fields()
         self.generate_altered_fields()
         self.generate_altered_order_with_respect_to()
         self.generate_altered_unique_together()
-        self.generate_altered_index_together()  # RemovedInDjango51Warning.
         self.generate_added_indexes()
         self.generate_added_constraints()
         self.generate_altered_db_table()
@@ -584,7 +582,7 @@ class MigrationAutodetector:
         possible).
 
         Defer any model options that refer to collections of fields that might
-        be deferred (e.g. unique_together, index_together).
+        be deferred (e.g. unique_together).
         """
         old_keys = self.old_model_keys | self.old_unmanaged_keys
         added_models = self.new_model_keys - old_keys
@@ -608,12 +606,10 @@ class MigrationAutodetector:
                     if getattr(field.remote_field, "through", None):
                         related_fields[field_name] = field
 
-            # Are there indexes/unique|index_together to defer?
+            # Are there indexes/unique_together to defer?
             indexes = model_state.options.pop("indexes")
             constraints = model_state.options.pop("constraints")
             unique_together = model_state.options.pop("unique_together", None)
-            # RemovedInDjango51Warning.
-            index_together = model_state.options.pop("index_together", None)
             order_with_respect_to = model_state.options.pop(
                 "order_with_respect_to", None
             )
@@ -742,16 +738,6 @@ class MigrationAutodetector:
                     ),
                     dependencies=related_dependencies,
                 )
-            # RemovedInDjango51Warning.
-            if index_together:
-                self.add_operation(
-                    app_label,
-                    operations.AlterIndexTogether(
-                        name=model_name,
-                        index_together=index_together,
-                    ),
-                    dependencies=related_dependencies,
-                )
             # Fix relationships if the model changed from a proxy model to a
             # concrete model.
             relations = self.to_state.relations
@@ -832,8 +818,6 @@ class MigrationAutodetector:
                         related_fields[field_name] = field
             # Generate option removal first
             unique_together = model_state.options.pop("unique_together", None)
-            # RemovedInDjango51Warning.
-            index_together = model_state.options.pop("index_together", None)
             if unique_together:
                 self.add_operation(
                     app_label,
@@ -842,15 +826,6 @@ class MigrationAutodetector:
                         unique_together=None,
                     ),
                 )
-            # RemovedInDjango51Warning.
-            if index_together:
-                self.add_operation(
-                    app_label,
-                    operations.AlterIndexTogether(
-                        name=model_name,
-                        index_together=None,
-                    ),
-                )
             # Then remove each related field
             for name in sorted(related_fields):
                 self.add_operation(
@@ -1525,10 +1500,6 @@ class MigrationAutodetector:
     def generate_removed_altered_unique_together(self):
         self._generate_removed_altered_foo_together(operations.AlterUniqueTogether)
 
-    # RemovedInDjango51Warning.
-    def generate_removed_altered_index_together(self):
-        self._generate_removed_altered_foo_together(operations.AlterIndexTogether)
-
     def _generate_altered_foo_together(self, operation):
         for (
             old_value,
@@ -1548,10 +1519,6 @@ class MigrationAutodetector:
     def generate_altered_unique_together(self):
         self._generate_altered_foo_together(operations.AlterUniqueTogether)
 
-    # RemovedInDjango51Warning.
-    def generate_altered_index_together(self):
-        self._generate_altered_foo_together(operations.AlterIndexTogether)
-
     def generate_altered_db_table(self):
         models_to_check = self.kept_model_keys.union(
             self.kept_proxy_keys, self.kept_unmanaged_keys

+ 0 - 27
django/db/migrations/operations/models.py

@@ -341,33 +341,6 @@ class CreateModel(ModelOperation):
                         managers=self.managers,
                     ),
                 ]
-            elif isinstance(operation, RenameIndex) and operation.old_fields:
-                options_index_together = {
-                    fields
-                    for fields in self.options.get("index_together", [])
-                    if fields != operation.old_fields
-                }
-                if options_index_together:
-                    self.options["index_together"] = options_index_together
-                else:
-                    self.options.pop("index_together", None)
-                return [
-                    CreateModel(
-                        self.name,
-                        fields=self.fields,
-                        options={
-                            **self.options,
-                            "indexes": [
-                                *self.options.get("indexes", []),
-                                models.Index(
-                                    fields=operation.old_fields, name=operation.new_name
-                                ),
-                            ],
-                        },
-                        bases=self.bases,
-                        managers=self.managers,
-                    ),
-                ]
         return super().reduce(operation, app_label)
 
 

+ 6 - 7
django/db/migrations/state.py

@@ -310,14 +310,13 @@ class ProjectState:
                         for from_field_name in from_fields
                     ]
                 )
-        # Fix index/unique_together to refer to the new field.
+        # Fix unique_together to refer to the new field.
         options = model_state.options
-        for option in ("index_together", "unique_together"):
-            if option in options:
-                options[option] = [
-                    [new_name if n == old_name else n for n in together]
-                    for together in options[option]
-                ]
+        if "unique_together" in options:
+            options["unique_together"] = [
+                [new_name if n == old_name else n for n in together]
+                for together in options["unique_together"]
+            ]
         # Fix to_fields to refer to the new field.
         delay = True
         references = get_references(self, model_key, (old_name, found))

+ 0 - 31
django/db/models/base.py

@@ -1595,7 +1595,6 @@ class Model(AltersData, metaclass=ModelBase):
             if not clash_errors:
                 errors.extend(cls._check_column_name_clashes())
             errors += [
-                *cls._check_index_together(),
                 *cls._check_unique_together(),
                 *cls._check_indexes(databases),
                 *cls._check_ordering(),
@@ -1928,36 +1927,6 @@ class Model(AltersData, metaclass=ModelBase):
             )
         return errors
 
-    # RemovedInDjango51Warning.
-    @classmethod
-    def _check_index_together(cls):
-        """Check the value of "index_together" option."""
-        if not isinstance(cls._meta.index_together, (tuple, list)):
-            return [
-                checks.Error(
-                    "'index_together' must be a list or tuple.",
-                    obj=cls,
-                    id="models.E008",
-                )
-            ]
-
-        elif any(
-            not isinstance(fields, (tuple, list)) for fields in cls._meta.index_together
-        ):
-            return [
-                checks.Error(
-                    "All 'index_together' elements must be lists or tuples.",
-                    obj=cls,
-                    id="models.E009",
-                )
-            ]
-
-        else:
-            errors = []
-            for fields in cls._meta.index_together:
-                errors.extend(cls._check_local_fields(fields, "index_together"))
-            return errors
-
     @classmethod
     def _check_unique_together(cls):
         """Check the value of "unique_together" option."""

+ 0 - 11
django/db/models/options.py

@@ -1,7 +1,6 @@
 import bisect
 import copy
 import inspect
-import warnings
 from collections import defaultdict
 
 from django.apps import apps
@@ -11,7 +10,6 @@ from django.db import connections
 from django.db.models import AutoField, Manager, OrderWrt, UniqueConstraint
 from django.db.models.query_utils import PathInfo
 from django.utils.datastructures import ImmutableList, OrderedSet
-from django.utils.deprecation import RemovedInDjango51Warning
 from django.utils.functional import cached_property
 from django.utils.module_loading import import_string
 from django.utils.text import camel_case_to_spaces, format_lazy
@@ -43,7 +41,6 @@ DEFAULT_NAMES = (
     "proxy",
     "swappable",
     "auto_created",
-    "index_together",  # RemovedInDjango51Warning.
     "apps",
     "default_permissions",
     "select_on_save",
@@ -119,7 +116,6 @@ class Options:
         self.indexes = []
         self.constraints = []
         self.unique_together = []
-        self.index_together = []  # RemovedInDjango51Warning.
         self.select_on_save = False
         self.default_permissions = ("add", "change", "delete", "view")
         self.permissions = []
@@ -205,13 +201,6 @@ class Options:
                     self.original_attrs[attr_name] = getattr(self, attr_name)
 
             self.unique_together = normalize_together(self.unique_together)
-            self.index_together = normalize_together(self.index_together)
-            if self.index_together:
-                warnings.warn(
-                    f"'index_together' is deprecated. Use 'Meta.indexes' in "
-                    f"{self.label!r} instead.",
-                    RemovedInDjango51Warning,
-                )
             # App label/class name interpolation for names of constraints and
             # indexes.
             if not getattr(cls._meta, "abstract", False):

+ 10 - 8
docs/ref/checks.txt

@@ -345,21 +345,23 @@ Models
   ``<field name>`` from model ``<model>``.
 * **models.E007**: Field ``<field name>`` has column name ``<column name>``
   that is used by another field.
-* **models.E008**: ``index_together`` must be a list or tuple.
+* **models.E008**: ``index_together`` must be a list or tuple. *This check
+  appeared before Django 5.1.*
 * **models.E009**: All ``index_together`` elements must be lists or tuples.
+  *This check appeared before Django 5.1.*
 * **models.E010**: ``unique_together`` must be a list or tuple.
 * **models.E011**: All ``unique_together`` elements must be lists or tuples.
-* **models.E012**: ``constraints/indexes/index_together/unique_together``
-  refers to the nonexistent field ``<field name>``.
-* **models.E013**: ``constraints/indexes/index_together/unique_together``
-  refers to a ``ManyToManyField`` ``<field name>``, but ``ManyToManyField``\s
-  are not supported for that option.
+* **models.E012**: ``constraints/indexes/unique_together`` refers to the
+  nonexistent field ``<field name>``.
+* **models.E013**: ``constraints/indexes/unique_together`` refers to a
+  ``ManyToManyField`` ``<field name>``, but ``ManyToManyField``\s are not
+  supported for that option.
 * **models.E014**: ``ordering`` must be a tuple or list (even if you want to
   order by only one field).
 * **models.E015**: ``ordering`` refers to the nonexistent field, related field,
   or lookup ``<field name>``.
-* **models.E016**: ``constraints/indexes/index_together/unique_together``
-  refers to field ``<field_name>`` which is not local to model ``<model>``.
+* **models.E016**: ``constraints/indexes/unique_together`` refers to field
+  ``<field_name>`` which is not local to model ``<model>``.
 * **models.E017**: Proxy model ``<model>`` contains model fields.
 * **models.E018**: Autogenerated column name too long for field ``<field>``.
   Maximum length is ``<maximum length>`` for database ``<alias>``.

+ 1 - 1
docs/ref/migration-operations.txt

@@ -247,7 +247,7 @@ Removes the index named ``name`` from the model with ``model_name``.
 Renames an index in the database table for the model with ``model_name``.
 Exactly one of ``old_name`` and ``old_fields`` can be provided. ``old_fields``
 is an iterable of the strings, often corresponding to fields of
-:attr:`~django.db.models.Options.index_together`.
+``index_together`` (pre-Django 5.1 option).
 
 On databases that don't support an index renaming statement (SQLite and MariaDB
 < 10.5.2), the operation will drop and recreate the index, which can be

+ 0 - 23
docs/ref/models/options.txt

@@ -451,29 +451,6 @@ not be looking at your Django code. For example::
     The ``ValidationError`` raised during model validation when the constraint
     is violated has the ``unique_together`` error code.
 
-``index_together``
-------------------
-
-.. attribute:: Options.index_together
-
-    Sets of field names that, taken together, are indexed::
-
-        index_together = [
-            ["pub_date", "deadline"],
-        ]
-
-    This list of fields will be indexed together (i.e. the appropriate
-    ``CREATE INDEX`` statement will be issued.)
-
-    For convenience, ``index_together`` can be a single list when dealing with a single
-    set of fields::
-
-        index_together = ["pub_date", "deadline"]
-
-    .. deprecated:: 4.2
-
-        Use the :attr:`~Options.indexes` option instead.
-
 ``constraints``
 ---------------
 

+ 2 - 3
docs/ref/schema-editor.txt

@@ -114,9 +114,8 @@ the new value.
 
 .. method:: BaseDatabaseSchemaEditor.alter_index_together(model, old_index_together, new_index_together)
 
-Changes a model's :attr:`~django.db.models.Options.index_together` value; this
-will add or remove indexes from the model's table until they match the new
-value.
+Changes a model's ``index_together`` value; this will add or remove indexes
+from the model's table until they match the new value.
 
 ``alter_db_table()``
 --------------------

+ 2 - 2
docs/releases/1.11.txt

@@ -59,8 +59,8 @@ creating database indexes. Indexes are added to models using the
 
 The :class:`~django.db.models.Index` class creates a b-tree index, as if you
 used :attr:`~django.db.models.Field.db_index` on the model field or
-:attr:`~django.db.models.Options.index_together` on the model ``Meta`` class.
-It can be subclassed to support different index types, such as
+``index_together`` on the model ``Meta`` class. It can be subclassed to support
+different index types, such as
 :class:`~django.contrib.postgres.indexes.GinIndex`. It also allows defining the
 order (ASC/DESC) for the columns of the index.
 

+ 1 - 2
docs/releases/1.5.txt

@@ -329,8 +329,7 @@ Django 1.5 also includes several smaller improvements worth noting:
   session data in a non-default cache.
 
 * Multi-column indexes can now be created on models. Read the
-  :attr:`~django.db.models.Options.index_together` documentation for more
-  information.
+  ``index_together`` documentation for more information.
 
 * During Django's logging configuration verbose Deprecation warnings are
   enabled and warnings are captured into the logging system. Logged warnings

+ 2 - 2
docs/releases/1.7.txt

@@ -778,8 +778,8 @@ Models
   an error (before that, it would either result in a database error or
   incorrect data).
 
-* You can use a single list for :attr:`~django.db.models.Options.index_together`
-  (rather than a list of lists) when specifying a single set of fields.
+* You can use a single list for ``index_together`` (rather than a list of
+  lists) when specifying a single set of fields.
 
 * Custom intermediate models having more than one foreign key to any of the
   models participating in a many-to-many relationship are now permitted,

+ 2 - 2
docs/releases/4.1.txt

@@ -317,7 +317,7 @@ Migrations
 * The new :class:`~django.db.migrations.operations.RenameIndex` operation
   allows renaming indexes defined in the
   :attr:`Meta.indexes <django.db.models.Options.indexes>` or
-  :attr:`~django.db.models.Options.index_together` options.
+  ``index_together`` options.
 
 * The migrations autodetector now generates
   :class:`~django.db.migrations.operations.RenameIndex` operations instead of
@@ -327,7 +327,7 @@ Migrations
 * The migrations autodetector now generates
   :class:`~django.db.migrations.operations.RenameIndex` operations instead of
   ``AlterIndexTogether`` and ``AddIndex``, when moving indexes defined in the
-  :attr:`Meta.index_together <django.db.models.Options.index_together>` to the
+  ``Meta.index_together`` to the
   :attr:`Meta.indexes <django.db.models.Options.indexes>`.
 
 Models

+ 2 - 3
docs/releases/4.2.txt

@@ -477,9 +477,8 @@ Features deprecated in 4.2
 ``index_together`` option is deprecated in favor of ``indexes``
 ---------------------------------------------------------------
 
-The :attr:`Meta.index_together <django.db.models.Options.index_together>`
-option is deprecated in favor of the :attr:`~django.db.models.Options.indexes`
-option.
+The ``Meta.index_together`` option is deprecated in favor of the
+:attr:`~django.db.models.Options.indexes` option.
 
 Migrating existing ``index_together`` should be handled as a migration. For
 example::

+ 2 - 0
docs/releases/5.1.txt

@@ -252,3 +252,5 @@ See :ref:`deprecated-features-4.2` for details on these changes, including how
 to remove usage of these features.
 
 * The ``BaseUserManager.make_random_password()`` method is removed.
+
+* The model's ``Meta.index_together`` option is removed.

+ 2 - 27
tests/indexes/tests.py

@@ -3,26 +3,16 @@ from unittest import skipUnless
 
 from django.conf import settings
 from django.db import connection
-from django.db.models import (
-    CASCADE,
-    CharField,
-    DateTimeField,
-    ForeignKey,
-    Index,
-    Model,
-    Q,
-)
+from django.db.models import CASCADE, ForeignKey, Index, Q
 from django.db.models.functions import Lower
 from django.test import (
     TestCase,
     TransactionTestCase,
-    ignore_warnings,
     skipIfDBFeature,
     skipUnlessDBFeature,
 )
-from django.test.utils import isolate_apps, override_settings
+from django.test.utils import override_settings
 from django.utils import timezone
-from django.utils.deprecation import RemovedInDjango51Warning
 
 from .models import Article, ArticleTranslation, IndexedArticle2
 
@@ -80,21 +70,6 @@ class SchemaIndexesTests(TestCase):
             index_sql[0],
         )
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    @isolate_apps("indexes")
-    def test_index_together_single_list(self):
-        class IndexTogetherSingleList(Model):
-            headline = CharField(max_length=100)
-            pub_date = DateTimeField()
-
-            class Meta:
-                index_together = ["headline", "pub_date"]
-
-        index_sql = connection.schema_editor()._model_indexes_sql(
-            IndexTogetherSingleList
-        )
-        self.assertEqual(len(index_sql), 1)
-
     def test_columns_list_sql(self):
         index = Index(fields=["headline"], name="whitespace_idx")
         editor = connection.schema_editor()

+ 1 - 130
tests/invalid_models_tests/test_models.py

@@ -5,9 +5,8 @@ from django.core.checks.model_checks import _check_lazy_references
 from django.db import connection, connections, models
 from django.db.models.functions import Abs, Lower, Round
 from django.db.models.signals import post_init
-from django.test import SimpleTestCase, TestCase, ignore_warnings, skipUnlessDBFeature
+from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
 from django.test.utils import isolate_apps, override_settings, register_lookup
-from django.utils.deprecation import RemovedInDjango51Warning
 
 
 class EmptyRouter:
@@ -29,134 +28,6 @@ def get_max_column_name_length():
     return (allowed_len, db_alias)
 
 
-@isolate_apps("invalid_models_tests")
-@ignore_warnings(category=RemovedInDjango51Warning)
-class IndexTogetherTests(SimpleTestCase):
-    def test_non_iterable(self):
-        class Model(models.Model):
-            class Meta:
-                index_together = 42
-
-        self.assertEqual(
-            Model.check(),
-            [
-                Error(
-                    "'index_together' must be a list or tuple.",
-                    obj=Model,
-                    id="models.E008",
-                ),
-            ],
-        )
-
-    def test_non_list(self):
-        class Model(models.Model):
-            class Meta:
-                index_together = "not-a-list"
-
-        self.assertEqual(
-            Model.check(),
-            [
-                Error(
-                    "'index_together' must be a list or tuple.",
-                    obj=Model,
-                    id="models.E008",
-                ),
-            ],
-        )
-
-    def test_list_containing_non_iterable(self):
-        class Model(models.Model):
-            class Meta:
-                index_together = [("a", "b"), 42]
-
-        self.assertEqual(
-            Model.check(),
-            [
-                Error(
-                    "All 'index_together' elements must be lists or tuples.",
-                    obj=Model,
-                    id="models.E009",
-                ),
-            ],
-        )
-
-    def test_pointing_to_missing_field(self):
-        class Model(models.Model):
-            class Meta:
-                index_together = [["missing_field"]]
-
-        self.assertEqual(
-            Model.check(),
-            [
-                Error(
-                    "'index_together' refers to the nonexistent field 'missing_field'.",
-                    obj=Model,
-                    id="models.E012",
-                ),
-            ],
-        )
-
-    def test_pointing_to_non_local_field(self):
-        class Foo(models.Model):
-            field1 = models.IntegerField()
-
-        class Bar(Foo):
-            field2 = models.IntegerField()
-
-            class Meta:
-                index_together = [["field2", "field1"]]
-
-        self.assertEqual(
-            Bar.check(),
-            [
-                Error(
-                    "'index_together' refers to field 'field1' which is not "
-                    "local to model 'Bar'.",
-                    hint="This issue may be caused by multi-table inheritance.",
-                    obj=Bar,
-                    id="models.E016",
-                ),
-            ],
-        )
-
-    def test_pointing_to_m2m_field(self):
-        class Model(models.Model):
-            m2m = models.ManyToManyField("self")
-
-            class Meta:
-                index_together = [["m2m"]]
-
-        self.assertEqual(
-            Model.check(),
-            [
-                Error(
-                    "'index_together' refers to a ManyToManyField 'm2m', but "
-                    "ManyToManyFields are not permitted in 'index_together'.",
-                    obj=Model,
-                    id="models.E013",
-                ),
-            ],
-        )
-
-    def test_pointing_to_fk(self):
-        class Foo(models.Model):
-            pass
-
-        class Bar(models.Model):
-            foo_1 = models.ForeignKey(
-                Foo, on_delete=models.CASCADE, related_name="bar_1"
-            )
-            foo_2 = models.ForeignKey(
-                Foo, on_delete=models.CASCADE, related_name="bar_2"
-            )
-
-            class Meta:
-                index_together = [["foo_1_id", "foo_2"]]
-
-        self.assertEqual(Bar.check(), [])
-
-
-# unique_together tests are very similar to index_together tests.
 @isolate_apps("invalid_models_tests")
 class UniqueTogetherTests(SimpleTestCase):
     def test_non_iterable(self):

+ 1 - 588
tests/migrations/test_autodetector.py

@@ -13,9 +13,8 @@ from django.db.migrations.graph import MigrationGraph
 from django.db.migrations.loader import MigrationLoader
 from django.db.migrations.questioner import MigrationQuestioner
 from django.db.migrations.state import ModelState, ProjectState
-from django.test import SimpleTestCase, TestCase, ignore_warnings, override_settings
+from django.test import SimpleTestCase, TestCase, override_settings
 from django.test.utils import isolate_lru_cache
-from django.utils.deprecation import RemovedInDjango51Warning
 
 from .models import FoodManager, FoodQuerySet
 
@@ -4872,592 +4871,6 @@ class AutodetectorTests(BaseAutodetectorTests):
         self.assertOperationAttributes(changes, "testapp", 0, 0, name="Book")
 
 
-@ignore_warnings(category=RemovedInDjango51Warning)
-class AutodetectorIndexTogetherTests(BaseAutodetectorTests):
-    book_index_together = ModelState(
-        "otherapp",
-        "Book",
-        [
-            ("id", models.AutoField(primary_key=True)),
-            ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
-            ("title", models.CharField(max_length=200)),
-        ],
-        {
-            "index_together": {("author", "title")},
-        },
-    )
-    book_index_together_2 = ModelState(
-        "otherapp",
-        "Book",
-        [
-            ("id", models.AutoField(primary_key=True)),
-            ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
-            ("title", models.CharField(max_length=200)),
-        ],
-        {
-            "index_together": {("title", "author")},
-        },
-    )
-    book_index_together_3 = ModelState(
-        "otherapp",
-        "Book",
-        [
-            ("id", models.AutoField(primary_key=True)),
-            ("newfield", models.IntegerField()),
-            ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
-            ("title", models.CharField(max_length=200)),
-        ],
-        {
-            "index_together": {("title", "newfield")},
-        },
-    )
-    book_index_together_4 = ModelState(
-        "otherapp",
-        "Book",
-        [
-            ("id", models.AutoField(primary_key=True)),
-            ("newfield2", models.IntegerField()),
-            ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
-            ("title", models.CharField(max_length=200)),
-        ],
-        {
-            "index_together": {("title", "newfield2")},
-        },
-    )
-
-    def test_empty_index_together(self):
-        """Empty index_together shouldn't generate a migration."""
-        # Explicitly testing for not specified, since this is the case after
-        # a CreateModel operation w/o any definition on the original model
-        model_state_not_specified = ModelState(
-            "a", "model", [("id", models.AutoField(primary_key=True))]
-        )
-        # Explicitly testing for None, since this was the issue in #23452 after
-        # an AlterIndexTogether operation with e.g. () as value
-        model_state_none = ModelState(
-            "a",
-            "model",
-            [("id", models.AutoField(primary_key=True))],
-            {
-                "index_together": None,
-            },
-        )
-        # Explicitly testing for the empty set, since we now always have sets.
-        # During removal (('col1', 'col2'),) --> () this becomes set([])
-        model_state_empty = ModelState(
-            "a",
-            "model",
-            [("id", models.AutoField(primary_key=True))],
-            {
-                "index_together": set(),
-            },
-        )
-
-        def test(from_state, to_state, msg):
-            changes = self.get_changes([from_state], [to_state])
-            if changes:
-                ops = ", ".join(
-                    o.__class__.__name__ for o in changes["a"][0].operations
-                )
-                self.fail("Created operation(s) %s from %s" % (ops, msg))
-
-        tests = (
-            (
-                model_state_not_specified,
-                model_state_not_specified,
-                '"not specified" to "not specified"',
-            ),
-            (model_state_not_specified, model_state_none, '"not specified" to "None"'),
-            (
-                model_state_not_specified,
-                model_state_empty,
-                '"not specified" to "empty"',
-            ),
-            (model_state_none, model_state_not_specified, '"None" to "not specified"'),
-            (model_state_none, model_state_none, '"None" to "None"'),
-            (model_state_none, model_state_empty, '"None" to "empty"'),
-            (
-                model_state_empty,
-                model_state_not_specified,
-                '"empty" to "not specified"',
-            ),
-            (model_state_empty, model_state_none, '"empty" to "None"'),
-            (model_state_empty, model_state_empty, '"empty" to "empty"'),
-        )
-
-        for t in tests:
-            test(*t)
-
-    def test_rename_index_together_to_index(self):
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, AutodetectorTests.book_indexes],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(changes, "otherapp", 0, ["RenameIndex"])
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            0,
-            model_name="book",
-            new_name="book_title_author_idx",
-            old_fields=("author", "title"),
-        )
-
-    def test_rename_index_together_to_index_extra_options(self):
-        # Indexes with extra options don't match indexes in index_together.
-        book_partial_index = ModelState(
-            "otherapp",
-            "Book",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
-                ("title", models.CharField(max_length=200)),
-            ],
-            {
-                "indexes": [
-                    models.Index(
-                        fields=["author", "title"],
-                        condition=models.Q(title__startswith="The"),
-                        name="book_title_author_idx",
-                    )
-                ],
-            },
-        )
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, book_partial_index],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AlterIndexTogether", "AddIndex"],
-        )
-
-    def test_rename_index_together_to_index_order_fields(self):
-        # Indexes with reordered fields don't match indexes in index_together.
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, AutodetectorTests.book_unordered_indexes],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AlterIndexTogether", "AddIndex"],
-        )
-
-    def test_add_index_together(self):
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, AutodetectorTests.book],
-            [AutodetectorTests.author_empty, self.book_index_together],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(changes, "otherapp", 0, ["AlterIndexTogether"])
-        self.assertOperationAttributes(
-            changes, "otherapp", 0, 0, name="book", index_together={("author", "title")}
-        )
-
-    def test_remove_index_together(self):
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, AutodetectorTests.book],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(changes, "otherapp", 0, ["AlterIndexTogether"])
-        self.assertOperationAttributes(
-            changes, "otherapp", 0, 0, name="book", index_together=set()
-        )
-
-    def test_index_together_remove_fk(self):
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, AutodetectorTests.book_with_no_author],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AlterIndexTogether", "RemoveField"],
-        )
-        self.assertOperationAttributes(
-            changes, "otherapp", 0, 0, name="book", index_together=set()
-        )
-        self.assertOperationAttributes(
-            changes, "otherapp", 0, 1, model_name="book", name="author"
-        )
-
-    def test_index_together_no_changes(self):
-        """
-        index_together doesn't generate a migration if no changes have been
-        made.
-        """
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, self.book_index_together],
-        )
-        self.assertEqual(len(changes), 0)
-
-    def test_index_together_ordering(self):
-        """index_together triggers on ordering changes."""
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together],
-            [AutodetectorTests.author_empty, self.book_index_together_2],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AlterIndexTogether"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            0,
-            name="book",
-            index_together={("title", "author")},
-        )
-
-    def test_add_field_and_index_together(self):
-        """
-        Added fields will be created before using them in index_together.
-        """
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, AutodetectorTests.book],
-            [AutodetectorTests.author_empty, self.book_index_together_3],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AddField", "AlterIndexTogether"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            1,
-            name="book",
-            index_together={("title", "newfield")},
-        )
-
-    def test_create_model_and_index_together(self):
-        author = ModelState(
-            "otherapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-            ],
-        )
-        book_with_author = ModelState(
-            "otherapp",
-            "Book",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("author", models.ForeignKey("otherapp.Author", models.CASCADE)),
-                ("title", models.CharField(max_length=200)),
-            ],
-            {
-                "index_together": {("title", "author")},
-            },
-        )
-        changes = self.get_changes(
-            [AutodetectorTests.book_with_no_author], [author, book_with_author]
-        )
-        self.assertEqual(len(changes["otherapp"]), 1)
-        migration = changes["otherapp"][0]
-        self.assertEqual(len(migration.operations), 3)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["CreateModel", "AddField", "AlterIndexTogether"],
-        )
-
-    def test_remove_field_and_index_together(self):
-        """
-        Removed fields will be removed after updating index_together.
-        """
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together_3],
-            [AutodetectorTests.author_empty, self.book_index_together],
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["AlterIndexTogether", "RemoveField"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            0,
-            name="book",
-            index_together={("author", "title")},
-        )
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            1,
-            model_name="book",
-            name="newfield",
-        )
-
-    def test_alter_field_and_index_together(self):
-        """Fields are altered after deleting some index_together."""
-        initial_author = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("age", models.IntegerField(db_index=True)),
-            ],
-            {
-                "index_together": {("name",)},
-            },
-        )
-        author_reversed_constraints = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200, unique=True)),
-                ("age", models.IntegerField()),
-            ],
-            {
-                "index_together": {("age",)},
-            },
-        )
-        changes = self.get_changes([initial_author], [author_reversed_constraints])
-
-        self.assertNumberMigrations(changes, "testapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "testapp",
-            0,
-            [
-                "AlterIndexTogether",
-                "AlterField",
-                "AlterField",
-                "AlterIndexTogether",
-            ],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            0,
-            name="author",
-            index_together=set(),
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            1,
-            model_name="author",
-            name="age",
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            2,
-            model_name="author",
-            name="name",
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            3,
-            name="author",
-            index_together={("age",)},
-        )
-
-    def test_partly_alter_index_together_increase(self):
-        initial_author = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("age", models.IntegerField()),
-            ],
-            {
-                "index_together": {("name",)},
-            },
-        )
-        author_new_constraints = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("age", models.IntegerField()),
-            ],
-            {
-                "index_together": {("name",), ("age",)},
-            },
-        )
-        changes = self.get_changes([initial_author], [author_new_constraints])
-
-        self.assertNumberMigrations(changes, "testapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "testapp",
-            0,
-            ["AlterIndexTogether"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            0,
-            name="author",
-            index_together={("name",), ("age",)},
-        )
-
-    def test_partly_alter_index_together_decrease(self):
-        initial_author = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("age", models.IntegerField()),
-            ],
-            {
-                "index_together": {("name",), ("age",)},
-            },
-        )
-        author_new_constraints = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("age", models.IntegerField()),
-            ],
-            {
-                "index_together": {("age",)},
-            },
-        )
-        changes = self.get_changes([initial_author], [author_new_constraints])
-
-        self.assertNumberMigrations(changes, "testapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "testapp",
-            0,
-            ["AlterIndexTogether"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            0,
-            name="author",
-            index_together={("age",)},
-        )
-
-    def test_rename_field_and_index_together(self):
-        """Fields are renamed before updating index_together."""
-        changes = self.get_changes(
-            [AutodetectorTests.author_empty, self.book_index_together_3],
-            [AutodetectorTests.author_empty, self.book_index_together_4],
-            MigrationQuestioner({"ask_rename": True}),
-        )
-        self.assertNumberMigrations(changes, "otherapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "otherapp",
-            0,
-            ["RenameField", "AlterIndexTogether"],
-        )
-        self.assertOperationAttributes(
-            changes,
-            "otherapp",
-            0,
-            1,
-            name="book",
-            index_together={("title", "newfield2")},
-        )
-
-    def test_add_model_order_with_respect_to_index_together(self):
-        changes = self.get_changes(
-            [],
-            [
-                AutodetectorTests.book,
-                ModelState(
-                    "testapp",
-                    "Author",
-                    [
-                        ("id", models.AutoField(primary_key=True)),
-                        ("name", models.CharField(max_length=200)),
-                        ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
-                    ],
-                    options={
-                        "order_with_respect_to": "book",
-                        "index_together": {("name", "_order")},
-                    },
-                ),
-            ],
-        )
-        self.assertNumberMigrations(changes, "testapp", 1)
-        self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
-        self.assertOperationAttributes(
-            changes,
-            "testapp",
-            0,
-            0,
-            name="Author",
-            options={
-                "order_with_respect_to": "book",
-                "index_together": {("name", "_order")},
-            },
-        )
-
-    def test_set_alter_order_with_respect_to_index_together(self):
-        after = ModelState(
-            "testapp",
-            "Author",
-            [
-                ("id", models.AutoField(primary_key=True)),
-                ("name", models.CharField(max_length=200)),
-                ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
-            ],
-            options={
-                "order_with_respect_to": "book",
-                "index_together": {("name", "_order")},
-            },
-        )
-        changes = self.get_changes(
-            [AutodetectorTests.book, AutodetectorTests.author_with_book],
-            [AutodetectorTests.book, after],
-        )
-        self.assertNumberMigrations(changes, "testapp", 1)
-        self.assertOperationTypes(
-            changes,
-            "testapp",
-            0,
-            ["AlterOrderWithRespectTo", "AlterIndexTogether"],
-        )
-
-
 class MigrationSuggestNameTests(SimpleTestCase):
     def test_no_operations(self):
         class Migration(migrations.Migration):

+ 0 - 3
tests/migrations/test_base.py

@@ -269,7 +269,6 @@ class OperationTestBase(MigrationTestBase):
         unique_together=False,
         options=False,
         db_table=None,
-        index_together=False,  # RemovedInDjango51Warning.
         constraints=None,
         indexes=None,
     ):
@@ -277,8 +276,6 @@ class OperationTestBase(MigrationTestBase):
         # Make the "current" state.
         model_options = {
             "swappable": "TEST_SWAP_MODEL",
-            # RemovedInDjango51Warning.
-            "index_together": [["weight", "pink"]] if index_together else [],
             "unique_together": [["pink", "weight"]] if unique_together else [],
         }
         if options:

+ 0 - 172
tests/migrations/test_operations.py

@@ -11,13 +11,11 @@ from django.db.models.functions import Abs, Pi
 from django.db.transaction import atomic
 from django.test import (
     SimpleTestCase,
-    ignore_warnings,
     override_settings,
     skipIfDBFeature,
     skipUnlessDBFeature,
 )
 from django.test.utils import CaptureQueriesContext
-from django.utils.deprecation import RemovedInDjango51Warning
 
 from .models import FoodManager, FoodQuerySet, UnicodeModel
 from .test_base import OperationTestBase
@@ -3094,37 +3092,6 @@ class OperationTests(OperationTestBase):
         self.assertColumnExists("test_rnflut_pony", "pink")
         self.assertColumnNotExists("test_rnflut_pony", "blue")
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    def test_rename_field_index_together(self):
-        project_state = self.set_up_test_model("test_rnflit", index_together=True)
-        operation = migrations.RenameField("Pony", "pink", "blue")
-        new_state = project_state.clone()
-        operation.state_forwards("test_rnflit", new_state)
-        self.assertIn("blue", new_state.models["test_rnflit", "pony"].fields)
-        self.assertNotIn("pink", new_state.models["test_rnflit", "pony"].fields)
-        # index_together has the renamed column.
-        self.assertIn(
-            "blue", new_state.models["test_rnflit", "pony"].options["index_together"][0]
-        )
-        self.assertNotIn(
-            "pink", new_state.models["test_rnflit", "pony"].options["index_together"][0]
-        )
-        # Rename field.
-        self.assertColumnExists("test_rnflit_pony", "pink")
-        self.assertColumnNotExists("test_rnflit_pony", "blue")
-        with connection.schema_editor() as editor:
-            operation.database_forwards("test_rnflit", editor, project_state, new_state)
-        self.assertColumnExists("test_rnflit_pony", "blue")
-        self.assertColumnNotExists("test_rnflit_pony", "pink")
-        # The index constraint has been ported over.
-        self.assertIndexExists("test_rnflit_pony", ["weight", "blue"])
-        # Reversal.
-        with connection.schema_editor() as editor:
-            operation.database_backwards(
-                "test_rnflit", editor, new_state, project_state
-            )
-        self.assertIndexExists("test_rnflit_pony", ["weight", "pink"])
-
     def test_rename_field_with_db_column(self):
         project_state = self.apply_operations(
             "test_rfwdbc",
@@ -3601,52 +3568,6 @@ class OperationTests(OperationTestBase):
         with self.assertRaisesMessage(ValueError, msg):
             migrations.RenameIndex("Pony", new_name="new_idx_name")
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    def test_rename_index_unnamed_index(self):
-        app_label = "test_rninui"
-        project_state = self.set_up_test_model(app_label, index_together=True)
-        table_name = app_label + "_pony"
-        self.assertIndexNameNotExists(table_name, "new_pony_test_idx")
-        operation = migrations.RenameIndex(
-            "Pony", new_name="new_pony_test_idx", old_fields=("weight", "pink")
-        )
-        self.assertEqual(
-            operation.describe(),
-            "Rename unnamed index for ('weight', 'pink') on Pony to new_pony_test_idx",
-        )
-        self.assertEqual(
-            operation.migration_name_fragment,
-            "rename_pony_weight_pink_new_pony_test_idx",
-        )
-
-        new_state = project_state.clone()
-        operation.state_forwards(app_label, new_state)
-        # Rename index.
-        with connection.schema_editor() as editor:
-            operation.database_forwards(app_label, editor, project_state, new_state)
-        self.assertIndexNameExists(table_name, "new_pony_test_idx")
-        # Reverse is a no-op.
-        with connection.schema_editor() as editor, self.assertNumQueries(0):
-            operation.database_backwards(app_label, editor, new_state, project_state)
-        self.assertIndexNameExists(table_name, "new_pony_test_idx")
-        # Reapply, RenameIndex operation is a noop when the old and new name
-        # match.
-        with connection.schema_editor() as editor:
-            operation.database_forwards(app_label, editor, new_state, project_state)
-        self.assertIndexNameExists(table_name, "new_pony_test_idx")
-        # Deconstruction.
-        definition = operation.deconstruct()
-        self.assertEqual(definition[0], "RenameIndex")
-        self.assertEqual(definition[1], [])
-        self.assertEqual(
-            definition[2],
-            {
-                "model_name": "Pony",
-                "new_name": "new_pony_test_idx",
-                "old_fields": ("weight", "pink"),
-            },
-        )
-
     def test_rename_index_unknown_unnamed_index(self):
         app_label = "test_rninuui"
         project_state = self.set_up_test_model(app_label)
@@ -3697,22 +3618,6 @@ class OperationTests(OperationTestBase):
         self.assertIsNot(old_model, new_model)
         self.assertEqual(new_model._meta.indexes[0].name, "new_pony_pink_idx")
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    def test_rename_index_state_forwards_unnamed_index(self):
-        app_label = "test_rnidsfui"
-        project_state = self.set_up_test_model(app_label, index_together=True)
-        old_model = project_state.apps.get_model(app_label, "Pony")
-        new_state = project_state.clone()
-
-        operation = migrations.RenameIndex(
-            "Pony", new_name="new_pony_pink_idx", old_fields=("weight", "pink")
-        )
-        operation.state_forwards(app_label, new_state)
-        new_model = new_state.apps.get_model(app_label, "Pony")
-        self.assertIsNot(old_model, new_model)
-        self.assertEqual(new_model._meta.index_together, tuple())
-        self.assertEqual(new_model._meta.indexes[0].name, "new_pony_pink_idx")
-
     @skipUnlessDBFeature("supports_expression_indexes")
     def test_add_func_index(self):
         app_label = "test_addfuncin"
@@ -3832,89 +3737,12 @@ class OperationTests(OperationTestBase):
         # Ensure the index is still there
         self.assertIndexExists("test_alflin_pony", ["pink"])
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    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")])
-        self.assertEqual(
-            operation.describe(), "Alter index_together for Pony (1 constraint(s))"
-        )
-        self.assertEqual(
-            operation.migration_name_fragment,
-            "alter_pony_index_together",
-        )
-        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"])
-        # And deconstruction
-        definition = operation.deconstruct()
-        self.assertEqual(definition[0], "AlterIndexTogether")
-        self.assertEqual(definition[1], [])
-        self.assertEqual(
-            definition[2], {"name": "Pony", "index_together": {("pink", "weight")}}
-        )
-
     def test_alter_index_together_remove(self):
         operation = migrations.AlterIndexTogether("Pony", None)
         self.assertEqual(
             operation.describe(), "Alter index_together for Pony (0 constraint(s))"
         )
 
-    @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    def test_alter_index_together_remove_with_unique_together(self):
-        app_label = "test_alintoremove_wunto"
-        table_name = "%s_pony" % app_label
-        project_state = self.set_up_test_model(app_label, unique_together=True)
-        self.assertUniqueConstraintExists(table_name, ["pink", "weight"])
-        # Add index together.
-        new_state = project_state.clone()
-        operation = migrations.AlterIndexTogether("Pony", [("pink", "weight")])
-        operation.state_forwards(app_label, new_state)
-        with connection.schema_editor() as editor:
-            operation.database_forwards(app_label, editor, project_state, new_state)
-        self.assertIndexExists(table_name, ["pink", "weight"])
-        # Remove index together.
-        project_state = new_state
-        new_state = project_state.clone()
-        operation = migrations.AlterIndexTogether("Pony", set())
-        operation.state_forwards(app_label, new_state)
-        with connection.schema_editor() as editor:
-            operation.database_forwards(app_label, editor, project_state, new_state)
-        self.assertIndexNotExists(table_name, ["pink", "weight"])
-        self.assertUniqueConstraintExists(table_name, ["pink", "weight"])
-
     def test_add_constraint(self):
         project_state = self.set_up_test_model("test_addconstraint")
         gt_check = models.Q(pink__gt=2)

+ 0 - 75
tests/migrations/test_optimizer.py

@@ -1293,81 +1293,6 @@ class OptimizerTests(SimpleTestCase):
             ],
         )
 
-    def test_create_model_remove_index_together_rename_index(self):
-        self.assertOptimizesTo(
-            [
-                migrations.CreateModel(
-                    name="Pony",
-                    fields=[
-                        ("weight", models.IntegerField()),
-                        ("age", models.IntegerField()),
-                    ],
-                    options={
-                        "index_together": [("age", "weight")],
-                    },
-                ),
-                migrations.RenameIndex(
-                    "Pony", new_name="idx_pony_age_weight", old_fields=("age", "weight")
-                ),
-            ],
-            [
-                migrations.CreateModel(
-                    name="Pony",
-                    fields=[
-                        ("weight", models.IntegerField()),
-                        ("age", models.IntegerField()),
-                    ],
-                    options={
-                        "indexes": [
-                            models.Index(
-                                fields=["age", "weight"], name="idx_pony_age_weight"
-                            ),
-                        ],
-                    },
-                ),
-            ],
-        )
-
-    def test_create_model_index_together_rename_index(self):
-        self.assertOptimizesTo(
-            [
-                migrations.CreateModel(
-                    name="Pony",
-                    fields=[
-                        ("weight", models.IntegerField()),
-                        ("age", models.IntegerField()),
-                        ("height", models.IntegerField()),
-                        ("rank", models.IntegerField()),
-                    ],
-                    options={
-                        "index_together": [("age", "weight"), ("height", "rank")],
-                    },
-                ),
-                migrations.RenameIndex(
-                    "Pony", new_name="idx_pony_age_weight", old_fields=("age", "weight")
-                ),
-            ],
-            [
-                migrations.CreateModel(
-                    name="Pony",
-                    fields=[
-                        ("weight", models.IntegerField()),
-                        ("age", models.IntegerField()),
-                        ("height", models.IntegerField()),
-                        ("rank", models.IntegerField()),
-                    ],
-                    options={
-                        "index_together": {("height", "rank")},
-                        "indexes": [
-                            models.Index(
-                                fields=["age", "weight"], name="idx_pony_age_weight"
-                            ),
-                        ],
-                    },
-                ),
-            ],
-        )
-
     def test_create_model_rename_index_no_old_fields(self):
         self.assertOptimizesTo(
             [

+ 1 - 7
tests/migrations/test_state.py

@@ -13,9 +13,8 @@ from django.db.migrations.state import (
     ProjectState,
     get_related_models_recursive,
 )
-from django.test import SimpleTestCase, ignore_warnings, override_settings
+from django.test import SimpleTestCase, override_settings
 from django.test.utils import isolate_apps
-from django.utils.deprecation import RemovedInDjango51Warning
 
 from .models import (
     FoodManager,
@@ -31,9 +30,6 @@ class StateTests(SimpleTestCase):
     Tests state construction, rendering and modification by operations.
     """
 
-    # RemovedInDjango51Warning, when deprecation ends, only remove
-    # Meta.index_together from inline models.
-    @ignore_warnings(category=RemovedInDjango51Warning)
     def test_create(self):
         """
         Tests making a ProjectState from an Apps
@@ -50,7 +46,6 @@ class StateTests(SimpleTestCase):
                 app_label = "migrations"
                 apps = new_apps
                 unique_together = ["name", "bio"]
-                index_together = ["bio", "age"]  # RemovedInDjango51Warning.
 
         class AuthorProxy(Author):
             class Meta:
@@ -142,7 +137,6 @@ class StateTests(SimpleTestCase):
             author_state.options,
             {
                 "unique_together": {("name", "bio")},
-                "index_together": {("bio", "age")},  # RemovedInDjango51Warning.
                 "indexes": [],
                 "constraints": [],
             },

+ 1 - 130
tests/schema/tests.py

@@ -54,14 +54,8 @@ from django.db.models.fields.json import KeyTextTransform
 from django.db.models.functions import Abs, Cast, Collate, Lower, Random, Upper
 from django.db.models.indexes import IndexExpression
 from django.db.transaction import TransactionManagementError, atomic
-from django.test import (
-    TransactionTestCase,
-    ignore_warnings,
-    skipIfDBFeature,
-    skipUnlessDBFeature,
-)
+from django.test import TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
 from django.test.utils import CaptureQueriesContext, isolate_apps, register_lookup
-from django.utils.deprecation import RemovedInDjango51Warning
 
 from .fields import CustomManyToManyField, InheritedManyToManyField, MediumBlobField
 from .models import (
@@ -3351,7 +3345,6 @@ class SchemaTests(TransactionTestCase):
             self.assertIsNone(editor.add_constraint(Author, constraint))
             self.assertIsNone(editor.remove_constraint(Author, constraint))
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
     def test_index_together(self):
         """
         Tests removing and adding index_together constraints on a model.
@@ -3395,128 +3388,6 @@ class SchemaTests(TransactionTestCase):
             False,
         )
 
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    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"]], [])
-
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    @isolate_apps("schema")
-    def test_create_index_together(self):
-        """
-        Tests creating models with index_together already defined
-        """
-
-        class TagIndexed(Model):
-            title = CharField(max_length=255)
-            slug = SlugField(unique=True)
-
-            class Meta:
-                app_label = "schema"
-                index_together = [["slug", "title"]]
-
-        # Create the table
-        with connection.schema_editor() as editor:
-            editor.create_model(TagIndexed)
-        self.isolated_local_models = [TagIndexed]
-        # Ensure there is an index
-        self.assertIs(
-            any(
-                c["index"]
-                for c in self.get_constraints("schema_tagindexed").values()
-                if c["columns"] == ["slug", "title"]
-            ),
-            True,
-        )
-
-    @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
-    @ignore_warnings(category=RemovedInDjango51Warning)
-    @isolate_apps("schema")
-    def test_remove_index_together_does_not_remove_meta_indexes(self):
-        class AuthorWithIndexedNameAndBirthday(Model):
-            name = CharField(max_length=255)
-            birthday = DateField()
-
-            class Meta:
-                app_label = "schema"
-                index_together = [["name", "birthday"]]
-
-        with connection.schema_editor() as editor:
-            editor.create_model(AuthorWithIndexedNameAndBirthday)
-        self.isolated_local_models = [AuthorWithIndexedNameAndBirthday]
-        # Add the custom index
-        index = Index(fields=["name", "birthday"], name="author_name_birthday_idx")
-        custom_index_name = index.name
-        AuthorWithIndexedNameAndBirthday._meta.indexes = [index]
-        with connection.schema_editor() as editor:
-            editor.add_index(AuthorWithIndexedNameAndBirthday, index)
-        # Ensure the indexes exist
-        constraints = self.get_constraints(
-            AuthorWithIndexedNameAndBirthday._meta.db_table
-        )
-        self.assertIn(custom_index_name, constraints)
-        other_constraints = [
-            name
-            for name, details in constraints.items()
-            if details["columns"] == ["name", "birthday"]
-            and details["index"]
-            and name != custom_index_name
-        ]
-        self.assertEqual(len(other_constraints), 1)
-        # Remove index together
-        index_together = AuthorWithIndexedNameAndBirthday._meta.index_together
-        with connection.schema_editor() as editor:
-            editor.alter_index_together(
-                AuthorWithIndexedNameAndBirthday, index_together, []
-            )
-        constraints = self.get_constraints(
-            AuthorWithIndexedNameAndBirthday._meta.db_table
-        )
-        self.assertIn(custom_index_name, constraints)
-        other_constraints = [
-            name
-            for name, details in constraints.items()
-            if details["columns"] == ["name", "birthday"]
-            and details["index"]
-            and name != custom_index_name
-        ]
-        self.assertEqual(len(other_constraints), 0)
-        # Re-add index together
-        with connection.schema_editor() as editor:
-            editor.alter_index_together(
-                AuthorWithIndexedNameAndBirthday, [], index_together
-            )
-        constraints = self.get_constraints(
-            AuthorWithIndexedNameAndBirthday._meta.db_table
-        )
-        self.assertIn(custom_index_name, constraints)
-        other_constraints = [
-            name
-            for name, details in constraints.items()
-            if details["columns"] == ["name", "birthday"]
-            and details["index"]
-            and name != custom_index_name
-        ]
-        self.assertEqual(len(other_constraints), 1)
-        # Drop the index
-        with connection.schema_editor() as editor:
-            AuthorWithIndexedNameAndBirthday._meta.indexes = []
-            editor.remove_index(AuthorWithIndexedNameAndBirthday, index)
-
     @isolate_apps("schema")
     def test_db_table(self):
         """