1. import datetime
    
  2. import itertools
    
  3. import unittest
    
  4. from copy import copy
    
  5. from unittest import mock
    
  6. 
    
  7. from django.core.exceptions import FieldError
    
  8. from django.core.management.color import no_style
    
  9. from django.db import (
    
  10.     DatabaseError,
    
  11.     DataError,
    
  12.     IntegrityError,
    
  13.     OperationalError,
    
  14.     connection,
    
  15. )
    
  16. from django.db.models import (
    
  17.     CASCADE,
    
  18.     PROTECT,
    
  19.     AutoField,
    
  20.     BigAutoField,
    
  21.     BigIntegerField,
    
  22.     BinaryField,
    
  23.     BooleanField,
    
  24.     CharField,
    
  25.     CheckConstraint,
    
  26.     DateField,
    
  27.     DateTimeField,
    
  28.     DecimalField,
    
  29.     DurationField,
    
  30.     F,
    
  31.     FloatField,
    
  32.     ForeignKey,
    
  33.     ForeignObject,
    
  34.     Index,
    
  35.     IntegerField,
    
  36.     JSONField,
    
  37.     ManyToManyField,
    
  38.     Model,
    
  39.     OneToOneField,
    
  40.     OrderBy,
    
  41.     PositiveIntegerField,
    
  42.     Q,
    
  43.     SlugField,
    
  44.     SmallAutoField,
    
  45.     SmallIntegerField,
    
  46.     TextField,
    
  47.     TimeField,
    
  48.     UniqueConstraint,
    
  49.     UUIDField,
    
  50.     Value,
    
  51. )
    
  52. from django.db.models.fields.json import KeyTextTransform
    
  53. from django.db.models.functions import Abs, Cast, Collate, Lower, Random, Upper
    
  54. from django.db.models.indexes import IndexExpression
    
  55. from django.db.transaction import TransactionManagementError, atomic
    
  56. from django.test import TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
    
  57. from django.test.utils import CaptureQueriesContext, isolate_apps, register_lookup
    
  58. 
    
  59. from .fields import CustomManyToManyField, InheritedManyToManyField, MediumBlobField
    
  60. from .models import (
    
  61.     Author,
    
  62.     AuthorCharFieldWithIndex,
    
  63.     AuthorTextFieldWithIndex,
    
  64.     AuthorWithDefaultHeight,
    
  65.     AuthorWithEvenLongerName,
    
  66.     AuthorWithIndexedName,
    
  67.     AuthorWithIndexedNameAndBirthday,
    
  68.     AuthorWithUniqueName,
    
  69.     AuthorWithUniqueNameAndBirthday,
    
  70.     Book,
    
  71.     BookForeignObj,
    
  72.     BookWeak,
    
  73.     BookWithLongName,
    
  74.     BookWithO2O,
    
  75.     BookWithoutAuthor,
    
  76.     BookWithSlug,
    
  77.     IntegerPK,
    
  78.     Node,
    
  79.     Note,
    
  80.     NoteRename,
    
  81.     Tag,
    
  82.     TagIndexed,
    
  83.     TagM2MTest,
    
  84.     TagUniqueRename,
    
  85.     Thing,
    
  86.     UniqueTest,
    
  87.     new_apps,
    
  88. )
    
  89. 
    
  90. 
    
  91. class SchemaTests(TransactionTestCase):
    
  92.     """
    
  93.     Tests for the schema-alteration code.
    
  94. 
    
  95.     Be aware that these tests are more liable than most to false results,
    
  96.     as sometimes the code to check if a test has worked is almost as complex
    
  97.     as the code it is testing.
    
  98.     """
    
  99. 
    
  100.     available_apps = []
    
  101. 
    
  102.     models = [
    
  103.         Author,
    
  104.         AuthorCharFieldWithIndex,
    
  105.         AuthorTextFieldWithIndex,
    
  106.         AuthorWithDefaultHeight,
    
  107.         AuthorWithEvenLongerName,
    
  108.         Book,
    
  109.         BookWeak,
    
  110.         BookWithLongName,
    
  111.         BookWithO2O,
    
  112.         BookWithSlug,
    
  113.         IntegerPK,
    
  114.         Node,
    
  115.         Note,
    
  116.         Tag,
    
  117.         TagIndexed,
    
  118.         TagM2MTest,
    
  119.         TagUniqueRename,
    
  120.         Thing,
    
  121.         UniqueTest,
    
  122.     ]
    
  123. 
    
  124.     # Utility functions
    
  125. 
    
  126.     def setUp(self):
    
  127.         # local_models should contain test dependent model classes that will be
    
  128.         # automatically removed from the app cache on test tear down.
    
  129.         self.local_models = []
    
  130.         # isolated_local_models contains models that are in test methods
    
  131.         # decorated with @isolate_apps.
    
  132.         self.isolated_local_models = []
    
  133. 
    
  134.     def tearDown(self):
    
  135.         # Delete any tables made for our models
    
  136.         self.delete_tables()
    
  137.         new_apps.clear_cache()
    
  138.         for model in new_apps.get_models():
    
  139.             model._meta._expire_cache()
    
  140.         if "schema" in new_apps.all_models:
    
  141.             for model in self.local_models:
    
  142.                 for many_to_many in model._meta.many_to_many:
    
  143.                     through = many_to_many.remote_field.through
    
  144.                     if through and through._meta.auto_created:
    
  145.                         del new_apps.all_models["schema"][through._meta.model_name]
    
  146.                 del new_apps.all_models["schema"][model._meta.model_name]
    
  147.         if self.isolated_local_models:
    
  148.             with connection.schema_editor() as editor:
    
  149.                 for model in self.isolated_local_models:
    
  150.                     editor.delete_model(model)
    
  151. 
    
  152.     def delete_tables(self):
    
  153.         "Deletes all model tables for our models for a clean test environment"
    
  154.         converter = connection.introspection.identifier_converter
    
  155.         with connection.schema_editor() as editor:
    
  156.             connection.disable_constraint_checking()
    
  157.             table_names = connection.introspection.table_names()
    
  158.             if connection.features.ignores_table_name_case:
    
  159.                 table_names = [table_name.lower() for table_name in table_names]
    
  160.             for model in itertools.chain(SchemaTests.models, self.local_models):
    
  161.                 tbl = converter(model._meta.db_table)
    
  162.                 if connection.features.ignores_table_name_case:
    
  163.                     tbl = tbl.lower()
    
  164.                 if tbl in table_names:
    
  165.                     editor.delete_model(model)
    
  166.                     table_names.remove(tbl)
    
  167.             connection.enable_constraint_checking()
    
  168. 
    
  169.     def column_classes(self, model):
    
  170.         with connection.cursor() as cursor:
    
  171.             columns = {
    
  172.                 d[0]: (connection.introspection.get_field_type(d[1], d), d)
    
  173.                 for d in connection.introspection.get_table_description(
    
  174.                     cursor,
    
  175.                     model._meta.db_table,
    
  176.                 )
    
  177.             }
    
  178.         # SQLite has a different format for field_type
    
  179.         for name, (type, desc) in columns.items():
    
  180.             if isinstance(type, tuple):
    
  181.                 columns[name] = (type[0], desc)
    
  182.         return columns
    
  183. 
    
  184.     def get_primary_key(self, table):
    
  185.         with connection.cursor() as cursor:
    
  186.             return connection.introspection.get_primary_key_column(cursor, table)
    
  187. 
    
  188.     def get_indexes(self, table):
    
  189.         """
    
  190.         Get the indexes on the table using a new cursor.
    
  191.         """
    
  192.         with connection.cursor() as cursor:
    
  193.             return [
    
  194.                 c["columns"][0]
    
  195.                 for c in connection.introspection.get_constraints(
    
  196.                     cursor, table
    
  197.                 ).values()
    
  198.                 if c["index"] and len(c["columns"]) == 1
    
  199.             ]
    
  200. 
    
  201.     def get_uniques(self, table):
    
  202.         with connection.cursor() as cursor:
    
  203.             return [
    
  204.                 c["columns"][0]
    
  205.                 for c in connection.introspection.get_constraints(
    
  206.                     cursor, table
    
  207.                 ).values()
    
  208.                 if c["unique"] and len(c["columns"]) == 1
    
  209.             ]
    
  210. 
    
  211.     def get_constraints(self, table):
    
  212.         """
    
  213.         Get the constraints on a table using a new cursor.
    
  214.         """
    
  215.         with connection.cursor() as cursor:
    
  216.             return connection.introspection.get_constraints(cursor, table)
    
  217. 
    
  218.     def get_constraints_for_column(self, model, column_name):
    
  219.         constraints = self.get_constraints(model._meta.db_table)
    
  220.         constraints_for_column = []
    
  221.         for name, details in constraints.items():
    
  222.             if details["columns"] == [column_name]:
    
  223.                 constraints_for_column.append(name)
    
  224.         return sorted(constraints_for_column)
    
  225. 
    
  226.     def check_added_field_default(
    
  227.         self,
    
  228.         schema_editor,
    
  229.         model,
    
  230.         field,
    
  231.         field_name,
    
  232.         expected_default,
    
  233.         cast_function=None,
    
  234.     ):
    
  235.         with connection.cursor() as cursor:
    
  236.             schema_editor.add_field(model, field)
    
  237.             cursor.execute(
    
  238.                 "SELECT {} FROM {};".format(field_name, model._meta.db_table)
    
  239.             )
    
  240.             database_default = cursor.fetchall()[0][0]
    
  241.             if cast_function and type(database_default) is not type(expected_default):
    
  242.                 database_default = cast_function(database_default)
    
  243.             self.assertEqual(database_default, expected_default)
    
  244. 
    
  245.     def get_constraints_count(self, table, column, fk_to):
    
  246.         """
    
  247.         Return a dict with keys 'fks', 'uniques, and 'indexes' indicating the
    
  248.         number of foreign keys, unique constraints, and indexes on
    
  249.         `table`.`column`. The `fk_to` argument is a 2-tuple specifying the
    
  250.         expected foreign key relationship's (table, column).
    
  251.         """
    
  252.         with connection.cursor() as cursor:
    
  253.             constraints = connection.introspection.get_constraints(cursor, table)
    
  254.         counts = {"fks": 0, "uniques": 0, "indexes": 0}
    
  255.         for c in constraints.values():
    
  256.             if c["columns"] == [column]:
    
  257.                 if c["foreign_key"] == fk_to:
    
  258.                     counts["fks"] += 1
    
  259.                 if c["unique"]:
    
  260.                     counts["uniques"] += 1
    
  261.                 elif c["index"]:
    
  262.                     counts["indexes"] += 1
    
  263.         return counts
    
  264. 
    
  265.     def get_column_collation(self, table, column):
    
  266.         with connection.cursor() as cursor:
    
  267.             return next(
    
  268.                 f.collation
    
  269.                 for f in connection.introspection.get_table_description(cursor, table)
    
  270.                 if f.name == column
    
  271.             )
    
  272. 
    
  273.     def assertIndexOrder(self, table, index, order):
    
  274.         constraints = self.get_constraints(table)
    
  275.         self.assertIn(index, constraints)
    
  276.         index_orders = constraints[index]["orders"]
    
  277.         self.assertTrue(
    
  278.             all(val == expected for val, expected in zip(index_orders, order))
    
  279.         )
    
  280. 
    
  281.     def assertForeignKeyExists(self, model, column, expected_fk_table, field="id"):
    
  282.         """
    
  283.         Fail if the FK constraint on `model.Meta.db_table`.`column` to
    
  284.         `expected_fk_table`.id doesn't exist.
    
  285.         """
    
  286.         if not connection.features.can_introspect_foreign_keys:
    
  287.             return
    
  288.         constraints = self.get_constraints(model._meta.db_table)
    
  289.         constraint_fk = None
    
  290.         for details in constraints.values():
    
  291.             if details["columns"] == [column] and details["foreign_key"]:
    
  292.                 constraint_fk = details["foreign_key"]
    
  293.                 break
    
  294.         self.assertEqual(constraint_fk, (expected_fk_table, field))
    
  295. 
    
  296.     def assertForeignKeyNotExists(self, model, column, expected_fk_table):
    
  297.         if not connection.features.can_introspect_foreign_keys:
    
  298.             return
    
  299.         with self.assertRaises(AssertionError):
    
  300.             self.assertForeignKeyExists(model, column, expected_fk_table)
    
  301. 
    
  302.     # Tests
    
  303.     def test_creation_deletion(self):
    
  304.         """
    
  305.         Tries creating a model's table, and then deleting it.
    
  306.         """
    
  307.         with connection.schema_editor() as editor:
    
  308.             # Create the table
    
  309.             editor.create_model(Author)
    
  310.             # The table is there
    
  311.             list(Author.objects.all())
    
  312.             # Clean up that table
    
  313.             editor.delete_model(Author)
    
  314.             # No deferred SQL should be left over.
    
  315.             self.assertEqual(editor.deferred_sql, [])
    
  316.         # The table is gone
    
  317.         with self.assertRaises(DatabaseError):
    
  318.             list(Author.objects.all())
    
  319. 
    
  320.     @skipUnlessDBFeature("supports_foreign_keys")
    
  321.     def test_fk(self):
    
  322.         "Creating tables out of FK order, then repointing, works"
    
  323.         # Create the table
    
  324.         with connection.schema_editor() as editor:
    
  325.             editor.create_model(Book)
    
  326.             editor.create_model(Author)
    
  327.             editor.create_model(Tag)
    
  328.         # Initial tables are there
    
  329.         list(Author.objects.all())
    
  330.         list(Book.objects.all())
    
  331.         # Make sure the FK constraint is present
    
  332.         with self.assertRaises(IntegrityError):
    
  333.             Book.objects.create(
    
  334.                 author_id=1,
    
  335.                 title="Much Ado About Foreign Keys",
    
  336.                 pub_date=datetime.datetime.now(),
    
  337.             )
    
  338.         # Repoint the FK constraint
    
  339.         old_field = Book._meta.get_field("author")
    
  340.         new_field = ForeignKey(Tag, CASCADE)
    
  341.         new_field.set_attributes_from_name("author")
    
  342.         with connection.schema_editor() as editor:
    
  343.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  344.         self.assertForeignKeyExists(Book, "author_id", "schema_tag")
    
  345. 
    
  346.     @skipUnlessDBFeature("can_create_inline_fk")
    
  347.     def test_inline_fk(self):
    
  348.         # Create some tables.
    
  349.         with connection.schema_editor() as editor:
    
  350.             editor.create_model(Author)
    
  351.             editor.create_model(Book)
    
  352.             editor.create_model(Note)
    
  353.         self.assertForeignKeyNotExists(Note, "book_id", "schema_book")
    
  354.         # Add a foreign key from one to the other.
    
  355.         with connection.schema_editor() as editor:
    
  356.             new_field = ForeignKey(Book, CASCADE)
    
  357.             new_field.set_attributes_from_name("book")
    
  358.             editor.add_field(Note, new_field)
    
  359.         self.assertForeignKeyExists(Note, "book_id", "schema_book")
    
  360.         # Creating a FK field with a constraint uses a single statement without
    
  361.         # a deferred ALTER TABLE.
    
  362.         self.assertFalse(
    
  363.             [
    
  364.                 sql
    
  365.                 for sql in (str(statement) for statement in editor.deferred_sql)
    
  366.                 if sql.startswith("ALTER TABLE") and "ADD CONSTRAINT" in sql
    
  367.             ]
    
  368.         )
    
  369. 
    
  370.     @skipUnlessDBFeature("can_create_inline_fk")
    
  371.     def test_add_inline_fk_update_data(self):
    
  372.         with connection.schema_editor() as editor:
    
  373.             editor.create_model(Node)
    
  374.         # Add an inline foreign key and update data in the same transaction.
    
  375.         new_field = ForeignKey(Node, CASCADE, related_name="new_fk", null=True)
    
  376.         new_field.set_attributes_from_name("new_parent_fk")
    
  377.         parent = Node.objects.create()
    
  378.         with connection.schema_editor() as editor:
    
  379.             editor.add_field(Node, new_field)
    
  380.             editor.execute("UPDATE schema_node SET new_parent_fk_id = %s;", [parent.pk])
    
  381.         assertIndex = (
    
  382.             self.assertIn
    
  383.             if connection.features.indexes_foreign_keys
    
  384.             else self.assertNotIn
    
  385.         )
    
  386.         assertIndex("new_parent_fk_id", self.get_indexes(Node._meta.db_table))
    
  387. 
    
  388.     @skipUnlessDBFeature(
    
  389.         "can_create_inline_fk",
    
  390.         "allows_multiple_constraints_on_same_fields",
    
  391.     )
    
  392.     @isolate_apps("schema")
    
  393.     def test_add_inline_fk_index_update_data(self):
    
  394.         class Node(Model):
    
  395.             class Meta:
    
  396.                 app_label = "schema"
    
  397. 
    
  398.         with connection.schema_editor() as editor:
    
  399.             editor.create_model(Node)
    
  400.         # Add an inline foreign key, update data, and an index in the same
    
  401.         # transaction.
    
  402.         new_field = ForeignKey(Node, CASCADE, related_name="new_fk", null=True)
    
  403.         new_field.set_attributes_from_name("new_parent_fk")
    
  404.         parent = Node.objects.create()
    
  405.         with connection.schema_editor() as editor:
    
  406.             editor.add_field(Node, new_field)
    
  407.             Node._meta.add_field(new_field)
    
  408.             editor.execute("UPDATE schema_node SET new_parent_fk_id = %s;", [parent.pk])
    
  409.             editor.add_index(
    
  410.                 Node, Index(fields=["new_parent_fk"], name="new_parent_inline_fk_idx")
    
  411.             )
    
  412.         self.assertIn("new_parent_fk_id", self.get_indexes(Node._meta.db_table))
    
  413. 
    
  414.     @skipUnlessDBFeature("supports_foreign_keys")
    
  415.     def test_char_field_with_db_index_to_fk(self):
    
  416.         # Create the table
    
  417.         with connection.schema_editor() as editor:
    
  418.             editor.create_model(Author)
    
  419.             editor.create_model(AuthorCharFieldWithIndex)
    
  420.         # Change CharField to FK
    
  421.         old_field = AuthorCharFieldWithIndex._meta.get_field("char_field")
    
  422.         new_field = ForeignKey(Author, CASCADE, blank=True)
    
  423.         new_field.set_attributes_from_name("char_field")
    
  424.         with connection.schema_editor() as editor:
    
  425.             editor.alter_field(
    
  426.                 AuthorCharFieldWithIndex, old_field, new_field, strict=True
    
  427.             )
    
  428.         self.assertForeignKeyExists(
    
  429.             AuthorCharFieldWithIndex, "char_field_id", "schema_author"
    
  430.         )
    
  431. 
    
  432.     @skipUnlessDBFeature("supports_foreign_keys")
    
  433.     @skipUnlessDBFeature("supports_index_on_text_field")
    
  434.     def test_text_field_with_db_index_to_fk(self):
    
  435.         # Create the table
    
  436.         with connection.schema_editor() as editor:
    
  437.             editor.create_model(Author)
    
  438.             editor.create_model(AuthorTextFieldWithIndex)
    
  439.         # Change TextField to FK
    
  440.         old_field = AuthorTextFieldWithIndex._meta.get_field("text_field")
    
  441.         new_field = ForeignKey(Author, CASCADE, blank=True)
    
  442.         new_field.set_attributes_from_name("text_field")
    
  443.         with connection.schema_editor() as editor:
    
  444.             editor.alter_field(
    
  445.                 AuthorTextFieldWithIndex, old_field, new_field, strict=True
    
  446.             )
    
  447.         self.assertForeignKeyExists(
    
  448.             AuthorTextFieldWithIndex, "text_field_id", "schema_author"
    
  449.         )
    
  450. 
    
  451.     @isolate_apps("schema")
    
  452.     def test_char_field_pk_to_auto_field(self):
    
  453.         class Foo(Model):
    
  454.             id = CharField(max_length=255, primary_key=True)
    
  455. 
    
  456.             class Meta:
    
  457.                 app_label = "schema"
    
  458. 
    
  459.         with connection.schema_editor() as editor:
    
  460.             editor.create_model(Foo)
    
  461.         self.isolated_local_models = [Foo]
    
  462.         old_field = Foo._meta.get_field("id")
    
  463.         new_field = AutoField(primary_key=True)
    
  464.         new_field.set_attributes_from_name("id")
    
  465.         new_field.model = Foo
    
  466.         with connection.schema_editor() as editor:
    
  467.             editor.alter_field(Foo, old_field, new_field, strict=True)
    
  468. 
    
  469.     @skipUnlessDBFeature("supports_foreign_keys")
    
  470.     def test_fk_to_proxy(self):
    
  471.         "Creating a FK to a proxy model creates database constraints."
    
  472. 
    
  473.         class AuthorProxy(Author):
    
  474.             class Meta:
    
  475.                 app_label = "schema"
    
  476.                 apps = new_apps
    
  477.                 proxy = True
    
  478. 
    
  479.         class AuthorRef(Model):
    
  480.             author = ForeignKey(AuthorProxy, on_delete=CASCADE)
    
  481. 
    
  482.             class Meta:
    
  483.                 app_label = "schema"
    
  484.                 apps = new_apps
    
  485. 
    
  486.         self.local_models = [AuthorProxy, AuthorRef]
    
  487. 
    
  488.         # Create the table
    
  489.         with connection.schema_editor() as editor:
    
  490.             editor.create_model(Author)
    
  491.             editor.create_model(AuthorRef)
    
  492.         self.assertForeignKeyExists(AuthorRef, "author_id", "schema_author")
    
  493. 
    
  494.     @skipUnlessDBFeature("supports_foreign_keys", "can_introspect_foreign_keys")
    
  495.     def test_fk_db_constraint(self):
    
  496.         "The db_constraint parameter is respected"
    
  497.         # Create the table
    
  498.         with connection.schema_editor() as editor:
    
  499.             editor.create_model(Tag)
    
  500.             editor.create_model(Author)
    
  501.             editor.create_model(BookWeak)
    
  502.         # Initial tables are there
    
  503.         list(Author.objects.all())
    
  504.         list(Tag.objects.all())
    
  505.         list(BookWeak.objects.all())
    
  506.         self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author")
    
  507.         # Make a db_constraint=False FK
    
  508.         new_field = ForeignKey(Tag, CASCADE, db_constraint=False)
    
  509.         new_field.set_attributes_from_name("tag")
    
  510.         with connection.schema_editor() as editor:
    
  511.             editor.add_field(Author, new_field)
    
  512.         self.assertForeignKeyNotExists(Author, "tag_id", "schema_tag")
    
  513.         # Alter to one with a constraint
    
  514.         new_field2 = ForeignKey(Tag, CASCADE)
    
  515.         new_field2.set_attributes_from_name("tag")
    
  516.         with connection.schema_editor() as editor:
    
  517.             editor.alter_field(Author, new_field, new_field2, strict=True)
    
  518.         self.assertForeignKeyExists(Author, "tag_id", "schema_tag")
    
  519.         # Alter to one without a constraint again
    
  520.         new_field2 = ForeignKey(Tag, CASCADE)
    
  521.         new_field2.set_attributes_from_name("tag")
    
  522.         with connection.schema_editor() as editor:
    
  523.             editor.alter_field(Author, new_field2, new_field, strict=True)
    
  524.         self.assertForeignKeyNotExists(Author, "tag_id", "schema_tag")
    
  525. 
    
  526.     @isolate_apps("schema")
    
  527.     def test_no_db_constraint_added_during_primary_key_change(self):
    
  528.         """
    
  529.         When a primary key that's pointed to by a ForeignKey with
    
  530.         db_constraint=False is altered, a foreign key constraint isn't added.
    
  531.         """
    
  532. 
    
  533.         class Author(Model):
    
  534.             class Meta:
    
  535.                 app_label = "schema"
    
  536. 
    
  537.         class BookWeak(Model):
    
  538.             author = ForeignKey(Author, CASCADE, db_constraint=False)
    
  539. 
    
  540.             class Meta:
    
  541.                 app_label = "schema"
    
  542. 
    
  543.         with connection.schema_editor() as editor:
    
  544.             editor.create_model(Author)
    
  545.             editor.create_model(BookWeak)
    
  546.         self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author")
    
  547.         old_field = Author._meta.get_field("id")
    
  548.         new_field = BigAutoField(primary_key=True)
    
  549.         new_field.model = Author
    
  550.         new_field.set_attributes_from_name("id")
    
  551.         # @isolate_apps() and inner models are needed to have the model
    
  552.         # relations populated, otherwise this doesn't act as a regression test.
    
  553.         self.assertEqual(len(new_field.model._meta.related_objects), 1)
    
  554.         with connection.schema_editor() as editor:
    
  555.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  556.         self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author")
    
  557. 
    
  558.     def _test_m2m_db_constraint(self, M2MFieldClass):
    
  559.         class LocalAuthorWithM2M(Model):
    
  560.             name = CharField(max_length=255)
    
  561. 
    
  562.             class Meta:
    
  563.                 app_label = "schema"
    
  564.                 apps = new_apps
    
  565. 
    
  566.         self.local_models = [LocalAuthorWithM2M]
    
  567. 
    
  568.         # Create the table
    
  569.         with connection.schema_editor() as editor:
    
  570.             editor.create_model(Tag)
    
  571.             editor.create_model(LocalAuthorWithM2M)
    
  572.         # Initial tables are there
    
  573.         list(LocalAuthorWithM2M.objects.all())
    
  574.         list(Tag.objects.all())
    
  575.         # Make a db_constraint=False FK
    
  576.         new_field = M2MFieldClass(Tag, related_name="authors", db_constraint=False)
    
  577.         new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
    
  578.         # Add the field
    
  579.         with connection.schema_editor() as editor:
    
  580.             editor.add_field(LocalAuthorWithM2M, new_field)
    
  581.         self.assertForeignKeyNotExists(
    
  582.             new_field.remote_field.through, "tag_id", "schema_tag"
    
  583.         )
    
  584. 
    
  585.     @skipUnlessDBFeature("supports_foreign_keys")
    
  586.     def test_m2m_db_constraint(self):
    
  587.         self._test_m2m_db_constraint(ManyToManyField)
    
  588. 
    
  589.     @skipUnlessDBFeature("supports_foreign_keys")
    
  590.     def test_m2m_db_constraint_custom(self):
    
  591.         self._test_m2m_db_constraint(CustomManyToManyField)
    
  592. 
    
  593.     @skipUnlessDBFeature("supports_foreign_keys")
    
  594.     def test_m2m_db_constraint_inherited(self):
    
  595.         self._test_m2m_db_constraint(InheritedManyToManyField)
    
  596. 
    
  597.     def test_add_field(self):
    
  598.         """
    
  599.         Tests adding fields to models
    
  600.         """
    
  601.         # Create the table
    
  602.         with connection.schema_editor() as editor:
    
  603.             editor.create_model(Author)
    
  604.         # Ensure there's no age field
    
  605.         columns = self.column_classes(Author)
    
  606.         self.assertNotIn("age", columns)
    
  607.         # Add the new field
    
  608.         new_field = IntegerField(null=True)
    
  609.         new_field.set_attributes_from_name("age")
    
  610.         with CaptureQueriesContext(
    
  611.             connection
    
  612.         ) as ctx, connection.schema_editor() as editor:
    
  613.             editor.add_field(Author, new_field)
    
  614.         drop_default_sql = editor.sql_alter_column_no_default % {
    
  615.             "column": editor.quote_name(new_field.name),
    
  616.         }
    
  617.         self.assertFalse(
    
  618.             any(drop_default_sql in query["sql"] for query in ctx.captured_queries)
    
  619.         )
    
  620.         # Table is not rebuilt.
    
  621.         self.assertIs(
    
  622.             any("CREATE TABLE" in query["sql"] for query in ctx.captured_queries), False
    
  623.         )
    
  624.         self.assertIs(
    
  625.             any("DROP TABLE" in query["sql"] for query in ctx.captured_queries), False
    
  626.         )
    
  627.         columns = self.column_classes(Author)
    
  628.         self.assertEqual(
    
  629.             columns["age"][0],
    
  630.             connection.features.introspected_field_types["IntegerField"],
    
  631.         )
    
  632.         self.assertTrue(columns["age"][1][6])
    
  633. 
    
  634.     def test_add_field_remove_field(self):
    
  635.         """
    
  636.         Adding a field and removing it removes all deferred sql referring to it.
    
  637.         """
    
  638.         with connection.schema_editor() as editor:
    
  639.             # Create a table with a unique constraint on the slug field.
    
  640.             editor.create_model(Tag)
    
  641.             # Remove the slug column.
    
  642.             editor.remove_field(Tag, Tag._meta.get_field("slug"))
    
  643.         self.assertEqual(editor.deferred_sql, [])
    
  644. 
    
  645.     def test_add_field_temp_default(self):
    
  646.         """
    
  647.         Tests adding fields to models with a temporary default
    
  648.         """
    
  649.         # Create the table
    
  650.         with connection.schema_editor() as editor:
    
  651.             editor.create_model(Author)
    
  652.         # Ensure there's no age field
    
  653.         columns = self.column_classes(Author)
    
  654.         self.assertNotIn("age", columns)
    
  655.         # Add some rows of data
    
  656.         Author.objects.create(name="Andrew", height=30)
    
  657.         Author.objects.create(name="Andrea")
    
  658.         # Add a not-null field
    
  659.         new_field = CharField(max_length=30, default="Godwin")
    
  660.         new_field.set_attributes_from_name("surname")
    
  661.         with connection.schema_editor() as editor:
    
  662.             editor.add_field(Author, new_field)
    
  663.         columns = self.column_classes(Author)
    
  664.         self.assertEqual(
    
  665.             columns["surname"][0],
    
  666.             connection.features.introspected_field_types["CharField"],
    
  667.         )
    
  668.         self.assertEqual(
    
  669.             columns["surname"][1][6],
    
  670.             connection.features.interprets_empty_strings_as_nulls,
    
  671.         )
    
  672. 
    
  673.     def test_add_field_temp_default_boolean(self):
    
  674.         """
    
  675.         Tests adding fields to models with a temporary default where
    
  676.         the default is False. (#21783)
    
  677.         """
    
  678.         # Create the table
    
  679.         with connection.schema_editor() as editor:
    
  680.             editor.create_model(Author)
    
  681.         # Ensure there's no age field
    
  682.         columns = self.column_classes(Author)
    
  683.         self.assertNotIn("age", columns)
    
  684.         # Add some rows of data
    
  685.         Author.objects.create(name="Andrew", height=30)
    
  686.         Author.objects.create(name="Andrea")
    
  687.         # Add a not-null field
    
  688.         new_field = BooleanField(default=False)
    
  689.         new_field.set_attributes_from_name("awesome")
    
  690.         with connection.schema_editor() as editor:
    
  691.             editor.add_field(Author, new_field)
    
  692.         columns = self.column_classes(Author)
    
  693.         # BooleanField are stored as TINYINT(1) on MySQL.
    
  694.         field_type = columns["awesome"][0]
    
  695.         self.assertEqual(
    
  696.             field_type, connection.features.introspected_field_types["BooleanField"]
    
  697.         )
    
  698. 
    
  699.     def test_add_field_default_transform(self):
    
  700.         """
    
  701.         Tests adding fields to models with a default that is not directly
    
  702.         valid in the database (#22581)
    
  703.         """
    
  704. 
    
  705.         class TestTransformField(IntegerField):
    
  706.             # Weird field that saves the count of items in its value
    
  707.             def get_default(self):
    
  708.                 return self.default
    
  709. 
    
  710.             def get_prep_value(self, value):
    
  711.                 if value is None:
    
  712.                     return 0
    
  713.                 return len(value)
    
  714. 
    
  715.         # Create the table
    
  716.         with connection.schema_editor() as editor:
    
  717.             editor.create_model(Author)
    
  718.         # Add some rows of data
    
  719.         Author.objects.create(name="Andrew", height=30)
    
  720.         Author.objects.create(name="Andrea")
    
  721.         # Add the field with a default it needs to cast (to string in this case)
    
  722.         new_field = TestTransformField(default={1: 2})
    
  723.         new_field.set_attributes_from_name("thing")
    
  724.         with connection.schema_editor() as editor:
    
  725.             editor.add_field(Author, new_field)
    
  726.         # Ensure the field is there
    
  727.         columns = self.column_classes(Author)
    
  728.         field_type, field_info = columns["thing"]
    
  729.         self.assertEqual(
    
  730.             field_type, connection.features.introspected_field_types["IntegerField"]
    
  731.         )
    
  732.         # Make sure the values were transformed correctly
    
  733.         self.assertEqual(Author.objects.extra(where=["thing = 1"]).count(), 2)
    
  734. 
    
  735.     def test_add_field_o2o_nullable(self):
    
  736.         with connection.schema_editor() as editor:
    
  737.             editor.create_model(Author)
    
  738.             editor.create_model(Note)
    
  739.         new_field = OneToOneField(Note, CASCADE, null=True)
    
  740.         new_field.set_attributes_from_name("note")
    
  741.         with connection.schema_editor() as editor:
    
  742.             editor.add_field(Author, new_field)
    
  743.         columns = self.column_classes(Author)
    
  744.         self.assertIn("note_id", columns)
    
  745.         self.assertTrue(columns["note_id"][1][6])
    
  746. 
    
  747.     def test_add_field_binary(self):
    
  748.         """
    
  749.         Tests binary fields get a sane default (#22851)
    
  750.         """
    
  751.         # Create the table
    
  752.         with connection.schema_editor() as editor:
    
  753.             editor.create_model(Author)
    
  754.         # Add the new field
    
  755.         new_field = BinaryField(blank=True)
    
  756.         new_field.set_attributes_from_name("bits")
    
  757.         with connection.schema_editor() as editor:
    
  758.             editor.add_field(Author, new_field)
    
  759.         columns = self.column_classes(Author)
    
  760.         # MySQL annoyingly uses the same backend, so it'll come back as one of
    
  761.         # these two types.
    
  762.         self.assertIn(columns["bits"][0], ("BinaryField", "TextField"))
    
  763. 
    
  764.     def test_add_field_durationfield_with_default(self):
    
  765.         with connection.schema_editor() as editor:
    
  766.             editor.create_model(Author)
    
  767.         new_field = DurationField(default=datetime.timedelta(minutes=10))
    
  768.         new_field.set_attributes_from_name("duration")
    
  769.         with connection.schema_editor() as editor:
    
  770.             editor.add_field(Author, new_field)
    
  771.         columns = self.column_classes(Author)
    
  772.         self.assertEqual(
    
  773.             columns["duration"][0],
    
  774.             connection.features.introspected_field_types["DurationField"],
    
  775.         )
    
  776. 
    
  777.     @unittest.skipUnless(connection.vendor == "mysql", "MySQL specific")
    
  778.     def test_add_binaryfield_mediumblob(self):
    
  779.         """
    
  780.         Test adding a custom-sized binary field on MySQL (#24846).
    
  781.         """
    
  782.         # Create the table
    
  783.         with connection.schema_editor() as editor:
    
  784.             editor.create_model(Author)
    
  785.         # Add the new field with default
    
  786.         new_field = MediumBlobField(blank=True, default=b"123")
    
  787.         new_field.set_attributes_from_name("bits")
    
  788.         with connection.schema_editor() as editor:
    
  789.             editor.add_field(Author, new_field)
    
  790.         columns = self.column_classes(Author)
    
  791.         # Introspection treats BLOBs as TextFields
    
  792.         self.assertEqual(columns["bits"][0], "TextField")
    
  793. 
    
  794.     @isolate_apps("schema")
    
  795.     def test_add_auto_field(self):
    
  796.         class AddAutoFieldModel(Model):
    
  797.             name = CharField(max_length=255, primary_key=True)
    
  798. 
    
  799.             class Meta:
    
  800.                 app_label = "schema"
    
  801. 
    
  802.         with connection.schema_editor() as editor:
    
  803.             editor.create_model(AddAutoFieldModel)
    
  804.         self.isolated_local_models = [AddAutoFieldModel]
    
  805.         old_field = AddAutoFieldModel._meta.get_field("name")
    
  806.         new_field = CharField(max_length=255)
    
  807.         new_field.set_attributes_from_name("name")
    
  808.         new_field.model = AddAutoFieldModel
    
  809.         with connection.schema_editor() as editor:
    
  810.             editor.alter_field(AddAutoFieldModel, old_field, new_field)
    
  811.         new_auto_field = AutoField(primary_key=True)
    
  812.         new_auto_field.set_attributes_from_name("id")
    
  813.         new_auto_field.model = AddAutoFieldModel()
    
  814.         with connection.schema_editor() as editor:
    
  815.             editor.add_field(AddAutoFieldModel, new_auto_field)
    
  816.         # Crashes on PostgreSQL when the GENERATED BY suffix is missing.
    
  817.         AddAutoFieldModel.objects.create(name="test")
    
  818. 
    
  819.     def test_remove_field(self):
    
  820.         with connection.schema_editor() as editor:
    
  821.             editor.create_model(Author)
    
  822.             with CaptureQueriesContext(connection) as ctx:
    
  823.                 editor.remove_field(Author, Author._meta.get_field("name"))
    
  824.         columns = self.column_classes(Author)
    
  825.         self.assertNotIn("name", columns)
    
  826.         if getattr(connection.features, "can_alter_table_drop_column", True):
    
  827.             # Table is not rebuilt.
    
  828.             self.assertIs(
    
  829.                 any("CREATE TABLE" in query["sql"] for query in ctx.captured_queries),
    
  830.                 False,
    
  831.             )
    
  832.             self.assertIs(
    
  833.                 any("DROP TABLE" in query["sql"] for query in ctx.captured_queries),
    
  834.                 False,
    
  835.             )
    
  836. 
    
  837.     def test_remove_indexed_field(self):
    
  838.         with connection.schema_editor() as editor:
    
  839.             editor.create_model(AuthorCharFieldWithIndex)
    
  840.         with connection.schema_editor() as editor:
    
  841.             editor.remove_field(
    
  842.                 AuthorCharFieldWithIndex,
    
  843.                 AuthorCharFieldWithIndex._meta.get_field("char_field"),
    
  844.             )
    
  845.         columns = self.column_classes(AuthorCharFieldWithIndex)
    
  846.         self.assertNotIn("char_field", columns)
    
  847. 
    
  848.     def test_alter(self):
    
  849.         """
    
  850.         Tests simple altering of fields
    
  851.         """
    
  852.         # Create the table
    
  853.         with connection.schema_editor() as editor:
    
  854.             editor.create_model(Author)
    
  855.         # Ensure the field is right to begin with
    
  856.         columns = self.column_classes(Author)
    
  857.         self.assertEqual(
    
  858.             columns["name"][0],
    
  859.             connection.features.introspected_field_types["CharField"],
    
  860.         )
    
  861.         self.assertEqual(
    
  862.             bool(columns["name"][1][6]),
    
  863.             bool(connection.features.interprets_empty_strings_as_nulls),
    
  864.         )
    
  865.         # Alter the name field to a TextField
    
  866.         old_field = Author._meta.get_field("name")
    
  867.         new_field = TextField(null=True)
    
  868.         new_field.set_attributes_from_name("name")
    
  869.         with connection.schema_editor() as editor:
    
  870.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  871.         columns = self.column_classes(Author)
    
  872.         self.assertEqual(columns["name"][0], "TextField")
    
  873.         self.assertTrue(columns["name"][1][6])
    
  874.         # Change nullability again
    
  875.         new_field2 = TextField(null=False)
    
  876.         new_field2.set_attributes_from_name("name")
    
  877.         with connection.schema_editor() as editor:
    
  878.             editor.alter_field(Author, new_field, new_field2, strict=True)
    
  879.         columns = self.column_classes(Author)
    
  880.         self.assertEqual(columns["name"][0], "TextField")
    
  881.         self.assertEqual(
    
  882.             bool(columns["name"][1][6]),
    
  883.             bool(connection.features.interprets_empty_strings_as_nulls),
    
  884.         )
    
  885. 
    
  886.     def test_alter_auto_field_to_integer_field(self):
    
  887.         # Create the table
    
  888.         with connection.schema_editor() as editor:
    
  889.             editor.create_model(Author)
    
  890.         # Change AutoField to IntegerField
    
  891.         old_field = Author._meta.get_field("id")
    
  892.         new_field = IntegerField(primary_key=True)
    
  893.         new_field.set_attributes_from_name("id")
    
  894.         new_field.model = Author
    
  895.         with connection.schema_editor() as editor:
    
  896.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  897.         # Now that ID is an IntegerField, the database raises an error if it
    
  898.         # isn't provided.
    
  899.         if not connection.features.supports_unspecified_pk:
    
  900.             with self.assertRaises(DatabaseError):
    
  901.                 Author.objects.create()
    
  902. 
    
  903.     def test_alter_auto_field_to_char_field(self):
    
  904.         # Create the table
    
  905.         with connection.schema_editor() as editor:
    
  906.             editor.create_model(Author)
    
  907.         # Change AutoField to CharField
    
  908.         old_field = Author._meta.get_field("id")
    
  909.         new_field = CharField(primary_key=True, max_length=50)
    
  910.         new_field.set_attributes_from_name("id")
    
  911.         new_field.model = Author
    
  912.         with connection.schema_editor() as editor:
    
  913.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  914. 
    
  915.     @isolate_apps("schema")
    
  916.     def test_alter_auto_field_quoted_db_column(self):
    
  917.         class Foo(Model):
    
  918.             id = AutoField(primary_key=True, db_column='"quoted_id"')
    
  919. 
    
  920.             class Meta:
    
  921.                 app_label = "schema"
    
  922. 
    
  923.         with connection.schema_editor() as editor:
    
  924.             editor.create_model(Foo)
    
  925.         self.isolated_local_models = [Foo]
    
  926.         old_field = Foo._meta.get_field("id")
    
  927.         new_field = BigAutoField(primary_key=True)
    
  928.         new_field.model = Foo
    
  929.         new_field.db_column = '"quoted_id"'
    
  930.         new_field.set_attributes_from_name("id")
    
  931.         with connection.schema_editor() as editor:
    
  932.             editor.alter_field(Foo, old_field, new_field, strict=True)
    
  933.         Foo.objects.create()
    
  934. 
    
  935.     def test_alter_not_unique_field_to_primary_key(self):
    
  936.         # Create the table.
    
  937.         with connection.schema_editor() as editor:
    
  938.             editor.create_model(Author)
    
  939.         # Change UUIDField to primary key.
    
  940.         old_field = Author._meta.get_field("uuid")
    
  941.         new_field = UUIDField(primary_key=True)
    
  942.         new_field.set_attributes_from_name("uuid")
    
  943.         new_field.model = Author
    
  944.         with connection.schema_editor() as editor:
    
  945.             editor.remove_field(Author, Author._meta.get_field("id"))
    
  946.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  947.         # Redundant unique constraint is not added.
    
  948.         count = self.get_constraints_count(
    
  949.             Author._meta.db_table,
    
  950.             Author._meta.get_field("uuid").column,
    
  951.             None,
    
  952.         )
    
  953.         self.assertLessEqual(count["uniques"], 1)
    
  954. 
    
  955.     @isolate_apps("schema")
    
  956.     def test_alter_primary_key_quoted_db_table(self):
    
  957.         class Foo(Model):
    
  958.             class Meta:
    
  959.                 app_label = "schema"
    
  960.                 db_table = '"foo"'
    
  961. 
    
  962.         with connection.schema_editor() as editor:
    
  963.             editor.create_model(Foo)
    
  964.         self.isolated_local_models = [Foo]
    
  965.         old_field = Foo._meta.get_field("id")
    
  966.         new_field = BigAutoField(primary_key=True)
    
  967.         new_field.model = Foo
    
  968.         new_field.set_attributes_from_name("id")
    
  969.         with connection.schema_editor() as editor:
    
  970.             editor.alter_field(Foo, old_field, new_field, strict=True)
    
  971.         Foo.objects.create()
    
  972. 
    
  973.     def test_alter_text_field(self):
    
  974.         # Regression for "BLOB/TEXT column 'info' can't have a default value")
    
  975.         # on MySQL.
    
  976.         # Create the table
    
  977.         with connection.schema_editor() as editor:
    
  978.             editor.create_model(Note)
    
  979.         old_field = Note._meta.get_field("info")
    
  980.         new_field = TextField(blank=True)
    
  981.         new_field.set_attributes_from_name("info")
    
  982.         with connection.schema_editor() as editor:
    
  983.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  984. 
    
  985.     def test_alter_text_field_to_not_null_with_default_value(self):
    
  986.         with connection.schema_editor() as editor:
    
  987.             editor.create_model(Note)
    
  988.         old_field = Note._meta.get_field("address")
    
  989.         new_field = TextField(blank=True, default="", null=False)
    
  990.         new_field.set_attributes_from_name("address")
    
  991.         with connection.schema_editor() as editor:
    
  992.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  993. 
    
  994.     @skipUnlessDBFeature("can_defer_constraint_checks", "can_rollback_ddl")
    
  995.     def test_alter_fk_checks_deferred_constraints(self):
    
  996.         """
    
  997.         #25492 - Altering a foreign key's structure and data in the same
    
  998.         transaction.
    
  999.         """
    
  1000.         with connection.schema_editor() as editor:
    
  1001.             editor.create_model(Node)
    
  1002.         old_field = Node._meta.get_field("parent")
    
  1003.         new_field = ForeignKey(Node, CASCADE)
    
  1004.         new_field.set_attributes_from_name("parent")
    
  1005.         parent = Node.objects.create()
    
  1006.         with connection.schema_editor() as editor:
    
  1007.             # Update the parent FK to create a deferred constraint check.
    
  1008.             Node.objects.update(parent=parent)
    
  1009.             editor.alter_field(Node, old_field, new_field, strict=True)
    
  1010. 
    
  1011.     @isolate_apps("schema")
    
  1012.     def test_alter_null_with_default_value_deferred_constraints(self):
    
  1013.         class Publisher(Model):
    
  1014.             class Meta:
    
  1015.                 app_label = "schema"
    
  1016. 
    
  1017.         class Article(Model):
    
  1018.             publisher = ForeignKey(Publisher, CASCADE)
    
  1019.             title = CharField(max_length=50, null=True)
    
  1020.             description = CharField(max_length=100, null=True)
    
  1021. 
    
  1022.             class Meta:
    
  1023.                 app_label = "schema"
    
  1024. 
    
  1025.         with connection.schema_editor() as editor:
    
  1026.             editor.create_model(Publisher)
    
  1027.             editor.create_model(Article)
    
  1028.         self.isolated_local_models = [Article, Publisher]
    
  1029. 
    
  1030.         publisher = Publisher.objects.create()
    
  1031.         Article.objects.create(publisher=publisher)
    
  1032. 
    
  1033.         old_title = Article._meta.get_field("title")
    
  1034.         new_title = CharField(max_length=50, null=False, default="")
    
  1035.         new_title.set_attributes_from_name("title")
    
  1036.         old_description = Article._meta.get_field("description")
    
  1037.         new_description = CharField(max_length=100, null=False, default="")
    
  1038.         new_description.set_attributes_from_name("description")
    
  1039.         with connection.schema_editor() as editor:
    
  1040.             editor.alter_field(Article, old_title, new_title, strict=True)
    
  1041.             editor.alter_field(Article, old_description, new_description, strict=True)
    
  1042. 
    
  1043.     def test_alter_text_field_to_date_field(self):
    
  1044.         """
    
  1045.         #25002 - Test conversion of text field to date field.
    
  1046.         """
    
  1047.         with connection.schema_editor() as editor:
    
  1048.             editor.create_model(Note)
    
  1049.         Note.objects.create(info="1988-05-05")
    
  1050.         old_field = Note._meta.get_field("info")
    
  1051.         new_field = DateField(blank=True)
    
  1052.         new_field.set_attributes_from_name("info")
    
  1053.         with connection.schema_editor() as editor:
    
  1054.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1055.         # Make sure the field isn't nullable
    
  1056.         columns = self.column_classes(Note)
    
  1057.         self.assertFalse(columns["info"][1][6])
    
  1058. 
    
  1059.     def test_alter_text_field_to_datetime_field(self):
    
  1060.         """
    
  1061.         #25002 - Test conversion of text field to datetime field.
    
  1062.         """
    
  1063.         with connection.schema_editor() as editor:
    
  1064.             editor.create_model(Note)
    
  1065.         Note.objects.create(info="1988-05-05 3:16:17.4567")
    
  1066.         old_field = Note._meta.get_field("info")
    
  1067.         new_field = DateTimeField(blank=True)
    
  1068.         new_field.set_attributes_from_name("info")
    
  1069.         with connection.schema_editor() as editor:
    
  1070.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1071.         # Make sure the field isn't nullable
    
  1072.         columns = self.column_classes(Note)
    
  1073.         self.assertFalse(columns["info"][1][6])
    
  1074. 
    
  1075.     def test_alter_text_field_to_time_field(self):
    
  1076.         """
    
  1077.         #25002 - Test conversion of text field to time field.
    
  1078.         """
    
  1079.         with connection.schema_editor() as editor:
    
  1080.             editor.create_model(Note)
    
  1081.         Note.objects.create(info="3:16:17.4567")
    
  1082.         old_field = Note._meta.get_field("info")
    
  1083.         new_field = TimeField(blank=True)
    
  1084.         new_field.set_attributes_from_name("info")
    
  1085.         with connection.schema_editor() as editor:
    
  1086.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1087.         # Make sure the field isn't nullable
    
  1088.         columns = self.column_classes(Note)
    
  1089.         self.assertFalse(columns["info"][1][6])
    
  1090. 
    
  1091.     @skipIfDBFeature("interprets_empty_strings_as_nulls")
    
  1092.     def test_alter_textual_field_keep_null_status(self):
    
  1093.         """
    
  1094.         Changing a field type shouldn't affect the not null status.
    
  1095.         """
    
  1096.         with connection.schema_editor() as editor:
    
  1097.             editor.create_model(Note)
    
  1098.         with self.assertRaises(IntegrityError):
    
  1099.             Note.objects.create(info=None)
    
  1100.         old_field = Note._meta.get_field("info")
    
  1101.         new_field = CharField(max_length=50)
    
  1102.         new_field.set_attributes_from_name("info")
    
  1103.         with connection.schema_editor() as editor:
    
  1104.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1105.         with self.assertRaises(IntegrityError):
    
  1106.             Note.objects.create(info=None)
    
  1107. 
    
  1108.     @skipUnlessDBFeature("interprets_empty_strings_as_nulls")
    
  1109.     def test_alter_textual_field_not_null_to_null(self):
    
  1110.         """
    
  1111.         Nullability for textual fields is preserved on databases that
    
  1112.         interpret empty strings as NULLs.
    
  1113.         """
    
  1114.         with connection.schema_editor() as editor:
    
  1115.             editor.create_model(Author)
    
  1116.         columns = self.column_classes(Author)
    
  1117.         # Field is nullable.
    
  1118.         self.assertTrue(columns["uuid"][1][6])
    
  1119.         # Change to NOT NULL.
    
  1120.         old_field = Author._meta.get_field("uuid")
    
  1121.         new_field = SlugField(null=False, blank=True)
    
  1122.         new_field.set_attributes_from_name("uuid")
    
  1123.         with connection.schema_editor() as editor:
    
  1124.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1125.         columns = self.column_classes(Author)
    
  1126.         # Nullability is preserved.
    
  1127.         self.assertTrue(columns["uuid"][1][6])
    
  1128. 
    
  1129.     def test_alter_numeric_field_keep_null_status(self):
    
  1130.         """
    
  1131.         Changing a field type shouldn't affect the not null status.
    
  1132.         """
    
  1133.         with connection.schema_editor() as editor:
    
  1134.             editor.create_model(UniqueTest)
    
  1135.         with self.assertRaises(IntegrityError):
    
  1136.             UniqueTest.objects.create(year=None, slug="aaa")
    
  1137.         old_field = UniqueTest._meta.get_field("year")
    
  1138.         new_field = BigIntegerField()
    
  1139.         new_field.set_attributes_from_name("year")
    
  1140.         with connection.schema_editor() as editor:
    
  1141.             editor.alter_field(UniqueTest, old_field, new_field, strict=True)
    
  1142.         with self.assertRaises(IntegrityError):
    
  1143.             UniqueTest.objects.create(year=None, slug="bbb")
    
  1144. 
    
  1145.     def test_alter_null_to_not_null(self):
    
  1146.         """
    
  1147.         #23609 - Tests handling of default values when altering from NULL to NOT NULL.
    
  1148.         """
    
  1149.         # Create the table
    
  1150.         with connection.schema_editor() as editor:
    
  1151.             editor.create_model(Author)
    
  1152.         # Ensure the field is right to begin with
    
  1153.         columns = self.column_classes(Author)
    
  1154.         self.assertTrue(columns["height"][1][6])
    
  1155.         # Create some test data
    
  1156.         Author.objects.create(name="Not null author", height=12)
    
  1157.         Author.objects.create(name="Null author")
    
  1158.         # Verify null value
    
  1159.         self.assertEqual(Author.objects.get(name="Not null author").height, 12)
    
  1160.         self.assertIsNone(Author.objects.get(name="Null author").height)
    
  1161.         # Alter the height field to NOT NULL with default
    
  1162.         old_field = Author._meta.get_field("height")
    
  1163.         new_field = PositiveIntegerField(default=42)
    
  1164.         new_field.set_attributes_from_name("height")
    
  1165.         with connection.schema_editor() as editor:
    
  1166.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1167.         columns = self.column_classes(Author)
    
  1168.         self.assertFalse(columns["height"][1][6])
    
  1169.         # Verify default value
    
  1170.         self.assertEqual(Author.objects.get(name="Not null author").height, 12)
    
  1171.         self.assertEqual(Author.objects.get(name="Null author").height, 42)
    
  1172. 
    
  1173.     def test_alter_charfield_to_null(self):
    
  1174.         """
    
  1175.         #24307 - Should skip an alter statement on databases with
    
  1176.         interprets_empty_strings_as_nulls when changing a CharField to null.
    
  1177.         """
    
  1178.         # Create the table
    
  1179.         with connection.schema_editor() as editor:
    
  1180.             editor.create_model(Author)
    
  1181.         # Change the CharField to null
    
  1182.         old_field = Author._meta.get_field("name")
    
  1183.         new_field = copy(old_field)
    
  1184.         new_field.null = True
    
  1185.         with connection.schema_editor() as editor:
    
  1186.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1187. 
    
  1188.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  1189.     def test_alter_char_field_decrease_length(self):
    
  1190.         # Create the table.
    
  1191.         with connection.schema_editor() as editor:
    
  1192.             editor.create_model(Author)
    
  1193.         Author.objects.create(name="x" * 255)
    
  1194.         # Change max_length of CharField.
    
  1195.         old_field = Author._meta.get_field("name")
    
  1196.         new_field = CharField(max_length=254)
    
  1197.         new_field.set_attributes_from_name("name")
    
  1198.         with connection.schema_editor() as editor:
    
  1199.             msg = "value too long for type character varying(254)"
    
  1200.             with self.assertRaisesMessage(DataError, msg):
    
  1201.                 editor.alter_field(Author, old_field, new_field, strict=True)
    
  1202. 
    
  1203.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  1204.     def test_alter_field_with_custom_db_type(self):
    
  1205.         from django.contrib.postgres.fields import ArrayField
    
  1206. 
    
  1207.         class Foo(Model):
    
  1208.             field = ArrayField(CharField(max_length=255))
    
  1209. 
    
  1210.             class Meta:
    
  1211.                 app_label = "schema"
    
  1212. 
    
  1213.         with connection.schema_editor() as editor:
    
  1214.             editor.create_model(Foo)
    
  1215.         self.isolated_local_models = [Foo]
    
  1216.         old_field = Foo._meta.get_field("field")
    
  1217.         new_field = ArrayField(CharField(max_length=16))
    
  1218.         new_field.set_attributes_from_name("field")
    
  1219.         new_field.model = Foo
    
  1220.         with connection.schema_editor() as editor:
    
  1221.             editor.alter_field(Foo, old_field, new_field, strict=True)
    
  1222. 
    
  1223.     @isolate_apps("schema")
    
  1224.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  1225.     def test_alter_array_field_decrease_base_field_length(self):
    
  1226.         from django.contrib.postgres.fields import ArrayField
    
  1227. 
    
  1228.         class ArrayModel(Model):
    
  1229.             field = ArrayField(CharField(max_length=16))
    
  1230. 
    
  1231.             class Meta:
    
  1232.                 app_label = "schema"
    
  1233. 
    
  1234.         with connection.schema_editor() as editor:
    
  1235.             editor.create_model(ArrayModel)
    
  1236.         self.isolated_local_models = [ArrayModel]
    
  1237.         ArrayModel.objects.create(field=["x" * 16])
    
  1238.         old_field = ArrayModel._meta.get_field("field")
    
  1239.         new_field = ArrayField(CharField(max_length=15))
    
  1240.         new_field.set_attributes_from_name("field")
    
  1241.         new_field.model = ArrayModel
    
  1242.         with connection.schema_editor() as editor:
    
  1243.             msg = "value too long for type character varying(15)"
    
  1244.             with self.assertRaisesMessage(DataError, msg):
    
  1245.                 editor.alter_field(ArrayModel, old_field, new_field, strict=True)
    
  1246. 
    
  1247.     @isolate_apps("schema")
    
  1248.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  1249.     def test_alter_array_field_decrease_nested_base_field_length(self):
    
  1250.         from django.contrib.postgres.fields import ArrayField
    
  1251. 
    
  1252.         class ArrayModel(Model):
    
  1253.             field = ArrayField(ArrayField(CharField(max_length=16)))
    
  1254. 
    
  1255.             class Meta:
    
  1256.                 app_label = "schema"
    
  1257. 
    
  1258.         with connection.schema_editor() as editor:
    
  1259.             editor.create_model(ArrayModel)
    
  1260.         self.isolated_local_models = [ArrayModel]
    
  1261.         ArrayModel.objects.create(field=[["x" * 16]])
    
  1262.         old_field = ArrayModel._meta.get_field("field")
    
  1263.         new_field = ArrayField(ArrayField(CharField(max_length=15)))
    
  1264.         new_field.set_attributes_from_name("field")
    
  1265.         new_field.model = ArrayModel
    
  1266.         with connection.schema_editor() as editor:
    
  1267.             msg = "value too long for type character varying(15)"
    
  1268.             with self.assertRaisesMessage(DataError, msg):
    
  1269.                 editor.alter_field(ArrayModel, old_field, new_field, strict=True)
    
  1270. 
    
  1271.     def test_alter_textfield_to_null(self):
    
  1272.         """
    
  1273.         #24307 - Should skip an alter statement on databases with
    
  1274.         interprets_empty_strings_as_nulls when changing a TextField to null.
    
  1275.         """
    
  1276.         # Create the table
    
  1277.         with connection.schema_editor() as editor:
    
  1278.             editor.create_model(Note)
    
  1279.         # Change the TextField to null
    
  1280.         old_field = Note._meta.get_field("info")
    
  1281.         new_field = copy(old_field)
    
  1282.         new_field.null = True
    
  1283.         with connection.schema_editor() as editor:
    
  1284.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1285. 
    
  1286.     def test_alter_null_to_not_null_keeping_default(self):
    
  1287.         """
    
  1288.         #23738 - Can change a nullable field with default to non-nullable
    
  1289.         with the same default.
    
  1290.         """
    
  1291.         # Create the table
    
  1292.         with connection.schema_editor() as editor:
    
  1293.             editor.create_model(AuthorWithDefaultHeight)
    
  1294.         # Ensure the field is right to begin with
    
  1295.         columns = self.column_classes(AuthorWithDefaultHeight)
    
  1296.         self.assertTrue(columns["height"][1][6])
    
  1297.         # Alter the height field to NOT NULL keeping the previous default
    
  1298.         old_field = AuthorWithDefaultHeight._meta.get_field("height")
    
  1299.         new_field = PositiveIntegerField(default=42)
    
  1300.         new_field.set_attributes_from_name("height")
    
  1301.         with connection.schema_editor() as editor:
    
  1302.             editor.alter_field(
    
  1303.                 AuthorWithDefaultHeight, old_field, new_field, strict=True
    
  1304.             )
    
  1305.         columns = self.column_classes(AuthorWithDefaultHeight)
    
  1306.         self.assertFalse(columns["height"][1][6])
    
  1307. 
    
  1308.     @skipUnlessDBFeature("supports_foreign_keys")
    
  1309.     def test_alter_fk(self):
    
  1310.         """
    
  1311.         Tests altering of FKs
    
  1312.         """
    
  1313.         # Create the table
    
  1314.         with connection.schema_editor() as editor:
    
  1315.             editor.create_model(Author)
    
  1316.             editor.create_model(Book)
    
  1317.         # Ensure the field is right to begin with
    
  1318.         columns = self.column_classes(Book)
    
  1319.         self.assertEqual(
    
  1320.             columns["author_id"][0],
    
  1321.             connection.features.introspected_field_types["IntegerField"],
    
  1322.         )
    
  1323.         self.assertForeignKeyExists(Book, "author_id", "schema_author")
    
  1324.         # Alter the FK
    
  1325.         old_field = Book._meta.get_field("author")
    
  1326.         new_field = ForeignKey(Author, CASCADE, editable=False)
    
  1327.         new_field.set_attributes_from_name("author")
    
  1328.         with connection.schema_editor() as editor:
    
  1329.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  1330.         columns = self.column_classes(Book)
    
  1331.         self.assertEqual(
    
  1332.             columns["author_id"][0],
    
  1333.             connection.features.introspected_field_types["IntegerField"],
    
  1334.         )
    
  1335.         self.assertForeignKeyExists(Book, "author_id", "schema_author")
    
  1336. 
    
  1337.     @skipUnlessDBFeature("supports_foreign_keys")
    
  1338.     def test_alter_to_fk(self):
    
  1339.         """
    
  1340.         #24447 - Tests adding a FK constraint for an existing column
    
  1341.         """
    
  1342. 
    
  1343.         class LocalBook(Model):
    
  1344.             author = IntegerField()
    
  1345.             title = CharField(max_length=100, db_index=True)
    
  1346.             pub_date = DateTimeField()
    
  1347. 
    
  1348.             class Meta:
    
  1349.                 app_label = "schema"
    
  1350.                 apps = new_apps
    
  1351. 
    
  1352.         self.local_models = [LocalBook]
    
  1353. 
    
  1354.         # Create the tables
    
  1355.         with connection.schema_editor() as editor:
    
  1356.             editor.create_model(Author)
    
  1357.             editor.create_model(LocalBook)
    
  1358.         # Ensure no FK constraint exists
    
  1359.         constraints = self.get_constraints(LocalBook._meta.db_table)
    
  1360.         for details in constraints.values():
    
  1361.             if details["foreign_key"]:
    
  1362.                 self.fail(
    
  1363.                     "Found an unexpected FK constraint to %s" % details["columns"]
    
  1364.                 )
    
  1365.         old_field = LocalBook._meta.get_field("author")
    
  1366.         new_field = ForeignKey(Author, CASCADE)
    
  1367.         new_field.set_attributes_from_name("author")
    
  1368.         with connection.schema_editor() as editor:
    
  1369.             editor.alter_field(LocalBook, old_field, new_field, strict=True)
    
  1370.         self.assertForeignKeyExists(LocalBook, "author_id", "schema_author")
    
  1371. 
    
  1372.     @skipUnlessDBFeature("supports_foreign_keys", "can_introspect_foreign_keys")
    
  1373.     def test_alter_o2o_to_fk(self):
    
  1374.         """
    
  1375.         #24163 - Tests altering of OneToOneField to ForeignKey
    
  1376.         """
    
  1377.         # Create the table
    
  1378.         with connection.schema_editor() as editor:
    
  1379.             editor.create_model(Author)
    
  1380.             editor.create_model(BookWithO2O)
    
  1381.         # Ensure the field is right to begin with
    
  1382.         columns = self.column_classes(BookWithO2O)
    
  1383.         self.assertEqual(
    
  1384.             columns["author_id"][0],
    
  1385.             connection.features.introspected_field_types["IntegerField"],
    
  1386.         )
    
  1387.         # Ensure the field is unique
    
  1388.         author = Author.objects.create(name="Joe")
    
  1389.         BookWithO2O.objects.create(
    
  1390.             author=author, title="Django 1", pub_date=datetime.datetime.now()
    
  1391.         )
    
  1392.         with self.assertRaises(IntegrityError):
    
  1393.             BookWithO2O.objects.create(
    
  1394.                 author=author, title="Django 2", pub_date=datetime.datetime.now()
    
  1395.             )
    
  1396.         BookWithO2O.objects.all().delete()
    
  1397.         self.assertForeignKeyExists(BookWithO2O, "author_id", "schema_author")
    
  1398.         # Alter the OneToOneField to ForeignKey
    
  1399.         old_field = BookWithO2O._meta.get_field("author")
    
  1400.         new_field = ForeignKey(Author, CASCADE)
    
  1401.         new_field.set_attributes_from_name("author")
    
  1402.         with connection.schema_editor() as editor:
    
  1403.             editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
    
  1404.         columns = self.column_classes(Book)
    
  1405.         self.assertEqual(
    
  1406.             columns["author_id"][0],
    
  1407.             connection.features.introspected_field_types["IntegerField"],
    
  1408.         )
    
  1409.         # Ensure the field is not unique anymore
    
  1410.         Book.objects.create(
    
  1411.             author=author, title="Django 1", pub_date=datetime.datetime.now()
    
  1412.         )
    
  1413.         Book.objects.create(
    
  1414.             author=author, title="Django 2", pub_date=datetime.datetime.now()
    
  1415.         )
    
  1416.         self.assertForeignKeyExists(Book, "author_id", "schema_author")
    
  1417. 
    
  1418.     @skipUnlessDBFeature("supports_foreign_keys", "can_introspect_foreign_keys")
    
  1419.     def test_alter_fk_to_o2o(self):
    
  1420.         """
    
  1421.         #24163 - Tests altering of ForeignKey to OneToOneField
    
  1422.         """
    
  1423.         # Create the table
    
  1424.         with connection.schema_editor() as editor:
    
  1425.             editor.create_model(Author)
    
  1426.             editor.create_model(Book)
    
  1427.         # Ensure the field is right to begin with
    
  1428.         columns = self.column_classes(Book)
    
  1429.         self.assertEqual(
    
  1430.             columns["author_id"][0],
    
  1431.             connection.features.introspected_field_types["IntegerField"],
    
  1432.         )
    
  1433.         # Ensure the field is not unique
    
  1434.         author = Author.objects.create(name="Joe")
    
  1435.         Book.objects.create(
    
  1436.             author=author, title="Django 1", pub_date=datetime.datetime.now()
    
  1437.         )
    
  1438.         Book.objects.create(
    
  1439.             author=author, title="Django 2", pub_date=datetime.datetime.now()
    
  1440.         )
    
  1441.         Book.objects.all().delete()
    
  1442.         self.assertForeignKeyExists(Book, "author_id", "schema_author")
    
  1443.         # Alter the ForeignKey to OneToOneField
    
  1444.         old_field = Book._meta.get_field("author")
    
  1445.         new_field = OneToOneField(Author, CASCADE)
    
  1446.         new_field.set_attributes_from_name("author")
    
  1447.         with connection.schema_editor() as editor:
    
  1448.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  1449.         columns = self.column_classes(BookWithO2O)
    
  1450.         self.assertEqual(
    
  1451.             columns["author_id"][0],
    
  1452.             connection.features.introspected_field_types["IntegerField"],
    
  1453.         )
    
  1454.         # Ensure the field is unique now
    
  1455.         BookWithO2O.objects.create(
    
  1456.             author=author, title="Django 1", pub_date=datetime.datetime.now()
    
  1457.         )
    
  1458.         with self.assertRaises(IntegrityError):
    
  1459.             BookWithO2O.objects.create(
    
  1460.                 author=author, title="Django 2", pub_date=datetime.datetime.now()
    
  1461.             )
    
  1462.         self.assertForeignKeyExists(BookWithO2O, "author_id", "schema_author")
    
  1463. 
    
  1464.     def test_alter_field_fk_to_o2o(self):
    
  1465.         with connection.schema_editor() as editor:
    
  1466.             editor.create_model(Author)
    
  1467.             editor.create_model(Book)
    
  1468.         expected_fks = (
    
  1469.             1
    
  1470.             if connection.features.supports_foreign_keys
    
  1471.             and connection.features.can_introspect_foreign_keys
    
  1472.             else 0
    
  1473.         )
    
  1474.         expected_indexes = 1 if connection.features.indexes_foreign_keys else 0
    
  1475. 
    
  1476.         # Check the index is right to begin with.
    
  1477.         counts = self.get_constraints_count(
    
  1478.             Book._meta.db_table,
    
  1479.             Book._meta.get_field("author").column,
    
  1480.             (Author._meta.db_table, Author._meta.pk.column),
    
  1481.         )
    
  1482.         self.assertEqual(
    
  1483.             counts,
    
  1484.             {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes},
    
  1485.         )
    
  1486. 
    
  1487.         old_field = Book._meta.get_field("author")
    
  1488.         new_field = OneToOneField(Author, CASCADE)
    
  1489.         new_field.set_attributes_from_name("author")
    
  1490.         with connection.schema_editor() as editor:
    
  1491.             editor.alter_field(Book, old_field, new_field)
    
  1492. 
    
  1493.         counts = self.get_constraints_count(
    
  1494.             Book._meta.db_table,
    
  1495.             Book._meta.get_field("author").column,
    
  1496.             (Author._meta.db_table, Author._meta.pk.column),
    
  1497.         )
    
  1498.         # The index on ForeignKey is replaced with a unique constraint for
    
  1499.         # OneToOneField.
    
  1500.         self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0})
    
  1501. 
    
  1502.     def test_autofield_to_o2o(self):
    
  1503.         with connection.schema_editor() as editor:
    
  1504.             editor.create_model(Author)
    
  1505.             editor.create_model(Note)
    
  1506. 
    
  1507.         # Rename the field.
    
  1508.         old_field = Author._meta.get_field("id")
    
  1509.         new_field = AutoField(primary_key=True)
    
  1510.         new_field.set_attributes_from_name("note_ptr")
    
  1511.         new_field.model = Author
    
  1512. 
    
  1513.         with connection.schema_editor() as editor:
    
  1514.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1515.         # Alter AutoField to OneToOneField.
    
  1516.         new_field_o2o = OneToOneField(Note, CASCADE)
    
  1517.         new_field_o2o.set_attributes_from_name("note_ptr")
    
  1518.         new_field_o2o.model = Author
    
  1519. 
    
  1520.         with connection.schema_editor() as editor:
    
  1521.             editor.alter_field(Author, new_field, new_field_o2o, strict=True)
    
  1522.         columns = self.column_classes(Author)
    
  1523.         field_type, _ = columns["note_ptr_id"]
    
  1524.         self.assertEqual(
    
  1525.             field_type, connection.features.introspected_field_types["IntegerField"]
    
  1526.         )
    
  1527. 
    
  1528.     def test_alter_field_fk_keeps_index(self):
    
  1529.         with connection.schema_editor() as editor:
    
  1530.             editor.create_model(Author)
    
  1531.             editor.create_model(Book)
    
  1532.         expected_fks = (
    
  1533.             1
    
  1534.             if connection.features.supports_foreign_keys
    
  1535.             and connection.features.can_introspect_foreign_keys
    
  1536.             else 0
    
  1537.         )
    
  1538.         expected_indexes = 1 if connection.features.indexes_foreign_keys else 0
    
  1539. 
    
  1540.         # Check the index is right to begin with.
    
  1541.         counts = self.get_constraints_count(
    
  1542.             Book._meta.db_table,
    
  1543.             Book._meta.get_field("author").column,
    
  1544.             (Author._meta.db_table, Author._meta.pk.column),
    
  1545.         )
    
  1546.         self.assertEqual(
    
  1547.             counts,
    
  1548.             {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes},
    
  1549.         )
    
  1550. 
    
  1551.         old_field = Book._meta.get_field("author")
    
  1552.         # on_delete changed from CASCADE.
    
  1553.         new_field = ForeignKey(Author, PROTECT)
    
  1554.         new_field.set_attributes_from_name("author")
    
  1555.         with connection.schema_editor() as editor:
    
  1556.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  1557. 
    
  1558.         counts = self.get_constraints_count(
    
  1559.             Book._meta.db_table,
    
  1560.             Book._meta.get_field("author").column,
    
  1561.             (Author._meta.db_table, Author._meta.pk.column),
    
  1562.         )
    
  1563.         # The index remains.
    
  1564.         self.assertEqual(
    
  1565.             counts,
    
  1566.             {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes},
    
  1567.         )
    
  1568. 
    
  1569.     def test_alter_field_o2o_to_fk(self):
    
  1570.         with connection.schema_editor() as editor:
    
  1571.             editor.create_model(Author)
    
  1572.             editor.create_model(BookWithO2O)
    
  1573.         expected_fks = (
    
  1574.             1
    
  1575.             if connection.features.supports_foreign_keys
    
  1576.             and connection.features.can_introspect_foreign_keys
    
  1577.             else 0
    
  1578.         )
    
  1579. 
    
  1580.         # Check the unique constraint is right to begin with.
    
  1581.         counts = self.get_constraints_count(
    
  1582.             BookWithO2O._meta.db_table,
    
  1583.             BookWithO2O._meta.get_field("author").column,
    
  1584.             (Author._meta.db_table, Author._meta.pk.column),
    
  1585.         )
    
  1586.         self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0})
    
  1587. 
    
  1588.         old_field = BookWithO2O._meta.get_field("author")
    
  1589.         new_field = ForeignKey(Author, CASCADE)
    
  1590.         new_field.set_attributes_from_name("author")
    
  1591.         with connection.schema_editor() as editor:
    
  1592.             editor.alter_field(BookWithO2O, old_field, new_field)
    
  1593. 
    
  1594.         counts = self.get_constraints_count(
    
  1595.             BookWithO2O._meta.db_table,
    
  1596.             BookWithO2O._meta.get_field("author").column,
    
  1597.             (Author._meta.db_table, Author._meta.pk.column),
    
  1598.         )
    
  1599.         # The unique constraint on OneToOneField is replaced with an index for
    
  1600.         # ForeignKey.
    
  1601.         self.assertEqual(counts, {"fks": expected_fks, "uniques": 0, "indexes": 1})
    
  1602. 
    
  1603.     def test_alter_field_o2o_keeps_unique(self):
    
  1604.         with connection.schema_editor() as editor:
    
  1605.             editor.create_model(Author)
    
  1606.             editor.create_model(BookWithO2O)
    
  1607.         expected_fks = (
    
  1608.             1
    
  1609.             if connection.features.supports_foreign_keys
    
  1610.             and connection.features.can_introspect_foreign_keys
    
  1611.             else 0
    
  1612.         )
    
  1613. 
    
  1614.         # Check the unique constraint is right to begin with.
    
  1615.         counts = self.get_constraints_count(
    
  1616.             BookWithO2O._meta.db_table,
    
  1617.             BookWithO2O._meta.get_field("author").column,
    
  1618.             (Author._meta.db_table, Author._meta.pk.column),
    
  1619.         )
    
  1620.         self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0})
    
  1621. 
    
  1622.         old_field = BookWithO2O._meta.get_field("author")
    
  1623.         # on_delete changed from CASCADE.
    
  1624.         new_field = OneToOneField(Author, PROTECT)
    
  1625.         new_field.set_attributes_from_name("author")
    
  1626.         with connection.schema_editor() as editor:
    
  1627.             editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
    
  1628. 
    
  1629.         counts = self.get_constraints_count(
    
  1630.             BookWithO2O._meta.db_table,
    
  1631.             BookWithO2O._meta.get_field("author").column,
    
  1632.             (Author._meta.db_table, Author._meta.pk.column),
    
  1633.         )
    
  1634.         # The unique constraint remains.
    
  1635.         self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0})
    
  1636. 
    
  1637.     @skipUnlessDBFeature("ignores_table_name_case")
    
  1638.     def test_alter_db_table_case(self):
    
  1639.         # Create the table
    
  1640.         with connection.schema_editor() as editor:
    
  1641.             editor.create_model(Author)
    
  1642.         # Alter the case of the table
    
  1643.         old_table_name = Author._meta.db_table
    
  1644.         with connection.schema_editor() as editor:
    
  1645.             editor.alter_db_table(Author, old_table_name, old_table_name.upper())
    
  1646. 
    
  1647.     def test_alter_implicit_id_to_explicit(self):
    
  1648.         """
    
  1649.         Should be able to convert an implicit "id" field to an explicit "id"
    
  1650.         primary key field.
    
  1651.         """
    
  1652.         with connection.schema_editor() as editor:
    
  1653.             editor.create_model(Author)
    
  1654. 
    
  1655.         old_field = Author._meta.get_field("id")
    
  1656.         new_field = AutoField(primary_key=True)
    
  1657.         new_field.set_attributes_from_name("id")
    
  1658.         new_field.model = Author
    
  1659.         with connection.schema_editor() as editor:
    
  1660.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1661.         # This will fail if DROP DEFAULT is inadvertently executed on this
    
  1662.         # field which drops the id sequence, at least on PostgreSQL.
    
  1663.         Author.objects.create(name="Foo")
    
  1664.         Author.objects.create(name="Bar")
    
  1665. 
    
  1666.     def test_alter_autofield_pk_to_bigautofield_pk(self):
    
  1667.         with connection.schema_editor() as editor:
    
  1668.             editor.create_model(Author)
    
  1669.         old_field = Author._meta.get_field("id")
    
  1670.         new_field = BigAutoField(primary_key=True)
    
  1671.         new_field.set_attributes_from_name("id")
    
  1672.         new_field.model = Author
    
  1673.         with connection.schema_editor() as editor:
    
  1674.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1675. 
    
  1676.         Author.objects.create(name="Foo", pk=1)
    
  1677.         with connection.cursor() as cursor:
    
  1678.             sequence_reset_sqls = connection.ops.sequence_reset_sql(
    
  1679.                 no_style(), [Author]
    
  1680.             )
    
  1681.             if sequence_reset_sqls:
    
  1682.                 cursor.execute(sequence_reset_sqls[0])
    
  1683.         self.assertIsNotNone(Author.objects.create(name="Bar"))
    
  1684. 
    
  1685.     def test_alter_autofield_pk_to_smallautofield_pk(self):
    
  1686.         with connection.schema_editor() as editor:
    
  1687.             editor.create_model(Author)
    
  1688.         old_field = Author._meta.get_field("id")
    
  1689.         new_field = SmallAutoField(primary_key=True)
    
  1690.         new_field.set_attributes_from_name("id")
    
  1691.         new_field.model = Author
    
  1692.         with connection.schema_editor() as editor:
    
  1693.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1694. 
    
  1695.         Author.objects.create(name="Foo", pk=1)
    
  1696.         with connection.cursor() as cursor:
    
  1697.             sequence_reset_sqls = connection.ops.sequence_reset_sql(
    
  1698.                 no_style(), [Author]
    
  1699.             )
    
  1700.             if sequence_reset_sqls:
    
  1701.                 cursor.execute(sequence_reset_sqls[0])
    
  1702.         self.assertIsNotNone(Author.objects.create(name="Bar"))
    
  1703. 
    
  1704.     def test_alter_int_pk_to_autofield_pk(self):
    
  1705.         """
    
  1706.         Should be able to rename an IntegerField(primary_key=True) to
    
  1707.         AutoField(primary_key=True).
    
  1708.         """
    
  1709.         with connection.schema_editor() as editor:
    
  1710.             editor.create_model(IntegerPK)
    
  1711. 
    
  1712.         old_field = IntegerPK._meta.get_field("i")
    
  1713.         new_field = AutoField(primary_key=True)
    
  1714.         new_field.model = IntegerPK
    
  1715.         new_field.set_attributes_from_name("i")
    
  1716. 
    
  1717.         with connection.schema_editor() as editor:
    
  1718.             editor.alter_field(IntegerPK, old_field, new_field, strict=True)
    
  1719. 
    
  1720.         # A model representing the updated model.
    
  1721.         class IntegerPKToAutoField(Model):
    
  1722.             i = AutoField(primary_key=True)
    
  1723.             j = IntegerField(unique=True)
    
  1724. 
    
  1725.             class Meta:
    
  1726.                 app_label = "schema"
    
  1727.                 apps = new_apps
    
  1728.                 db_table = IntegerPK._meta.db_table
    
  1729. 
    
  1730.         # An id (i) is generated by the database.
    
  1731.         obj = IntegerPKToAutoField.objects.create(j=1)
    
  1732.         self.assertIsNotNone(obj.i)
    
  1733. 
    
  1734.     def test_alter_int_pk_to_bigautofield_pk(self):
    
  1735.         """
    
  1736.         Should be able to rename an IntegerField(primary_key=True) to
    
  1737.         BigAutoField(primary_key=True).
    
  1738.         """
    
  1739.         with connection.schema_editor() as editor:
    
  1740.             editor.create_model(IntegerPK)
    
  1741. 
    
  1742.         old_field = IntegerPK._meta.get_field("i")
    
  1743.         new_field = BigAutoField(primary_key=True)
    
  1744.         new_field.model = IntegerPK
    
  1745.         new_field.set_attributes_from_name("i")
    
  1746. 
    
  1747.         with connection.schema_editor() as editor:
    
  1748.             editor.alter_field(IntegerPK, old_field, new_field, strict=True)
    
  1749. 
    
  1750.         # A model representing the updated model.
    
  1751.         class IntegerPKToBigAutoField(Model):
    
  1752.             i = BigAutoField(primary_key=True)
    
  1753.             j = IntegerField(unique=True)
    
  1754. 
    
  1755.             class Meta:
    
  1756.                 app_label = "schema"
    
  1757.                 apps = new_apps
    
  1758.                 db_table = IntegerPK._meta.db_table
    
  1759. 
    
  1760.         # An id (i) is generated by the database.
    
  1761.         obj = IntegerPKToBigAutoField.objects.create(j=1)
    
  1762.         self.assertIsNotNone(obj.i)
    
  1763. 
    
  1764.     @isolate_apps("schema")
    
  1765.     def test_alter_smallint_pk_to_smallautofield_pk(self):
    
  1766.         """
    
  1767.         Should be able to rename an SmallIntegerField(primary_key=True) to
    
  1768.         SmallAutoField(primary_key=True).
    
  1769.         """
    
  1770. 
    
  1771.         class SmallIntegerPK(Model):
    
  1772.             i = SmallIntegerField(primary_key=True)
    
  1773. 
    
  1774.             class Meta:
    
  1775.                 app_label = "schema"
    
  1776. 
    
  1777.         with connection.schema_editor() as editor:
    
  1778.             editor.create_model(SmallIntegerPK)
    
  1779.         self.isolated_local_models = [SmallIntegerPK]
    
  1780.         old_field = SmallIntegerPK._meta.get_field("i")
    
  1781.         new_field = SmallAutoField(primary_key=True)
    
  1782.         new_field.model = SmallIntegerPK
    
  1783.         new_field.set_attributes_from_name("i")
    
  1784.         with connection.schema_editor() as editor:
    
  1785.             editor.alter_field(SmallIntegerPK, old_field, new_field, strict=True)
    
  1786. 
    
  1787.     @isolate_apps("schema")
    
  1788.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  1789.     def test_alter_serial_auto_field_to_bigautofield(self):
    
  1790.         class SerialAutoField(Model):
    
  1791.             id = SmallAutoField(primary_key=True)
    
  1792. 
    
  1793.             class Meta:
    
  1794.                 app_label = "schema"
    
  1795. 
    
  1796.         table = SerialAutoField._meta.db_table
    
  1797.         column = SerialAutoField._meta.get_field("id").column
    
  1798.         with connection.cursor() as cursor:
    
  1799.             cursor.execute(
    
  1800.                 f'CREATE TABLE "{table}" '
    
  1801.                 f'("{column}" smallserial NOT NULL PRIMARY KEY)'
    
  1802.             )
    
  1803.         try:
    
  1804.             old_field = SerialAutoField._meta.get_field("id")
    
  1805.             new_field = BigAutoField(primary_key=True)
    
  1806.             new_field.model = SerialAutoField
    
  1807.             new_field.set_attributes_from_name("id")
    
  1808.             with connection.schema_editor() as editor:
    
  1809.                 editor.alter_field(SerialAutoField, old_field, new_field, strict=True)
    
  1810.             sequence_name = f"{table}_{column}_seq"
    
  1811.             with connection.cursor() as cursor:
    
  1812.                 cursor.execute(
    
  1813.                     "SELECT data_type FROM pg_sequences WHERE sequencename = %s",
    
  1814.                     [sequence_name],
    
  1815.                 )
    
  1816.                 row = cursor.fetchone()
    
  1817.                 sequence_data_type = row[0] if row and row[0] else None
    
  1818.                 self.assertEqual(sequence_data_type, "bigint")
    
  1819.             # Rename the column.
    
  1820.             old_field = new_field
    
  1821.             new_field = AutoField(primary_key=True)
    
  1822.             new_field.model = SerialAutoField
    
  1823.             new_field.set_attributes_from_name("renamed_id")
    
  1824.             with connection.schema_editor() as editor:
    
  1825.                 editor.alter_field(SerialAutoField, old_field, new_field, strict=True)
    
  1826.             with connection.cursor() as cursor:
    
  1827.                 cursor.execute(
    
  1828.                     "SELECT data_type FROM pg_sequences WHERE sequencename = %s",
    
  1829.                     [sequence_name],
    
  1830.                 )
    
  1831.                 row = cursor.fetchone()
    
  1832.                 sequence_data_type = row[0] if row and row[0] else None
    
  1833.                 self.assertEqual(sequence_data_type, "integer")
    
  1834.         finally:
    
  1835.             with connection.cursor() as cursor:
    
  1836.                 cursor.execute(f'DROP TABLE "{table}"')
    
  1837. 
    
  1838.     def test_alter_int_pk_to_int_unique(self):
    
  1839.         """
    
  1840.         Should be able to rename an IntegerField(primary_key=True) to
    
  1841.         IntegerField(unique=True).
    
  1842.         """
    
  1843.         with connection.schema_editor() as editor:
    
  1844.             editor.create_model(IntegerPK)
    
  1845.         # Delete the old PK
    
  1846.         old_field = IntegerPK._meta.get_field("i")
    
  1847.         new_field = IntegerField(unique=True)
    
  1848.         new_field.model = IntegerPK
    
  1849.         new_field.set_attributes_from_name("i")
    
  1850.         with connection.schema_editor() as editor:
    
  1851.             editor.alter_field(IntegerPK, old_field, new_field, strict=True)
    
  1852.         # The primary key constraint is gone. Result depends on database:
    
  1853.         # 'id' for SQLite, None for others (must not be 'i').
    
  1854.         self.assertIn(self.get_primary_key(IntegerPK._meta.db_table), ("id", None))
    
  1855. 
    
  1856.         # Set up a model class as it currently stands. The original IntegerPK
    
  1857.         # class is now out of date and some backends make use of the whole
    
  1858.         # model class when modifying a field (such as sqlite3 when remaking a
    
  1859.         # table) so an outdated model class leads to incorrect results.
    
  1860.         class Transitional(Model):
    
  1861.             i = IntegerField(unique=True)
    
  1862.             j = IntegerField(unique=True)
    
  1863. 
    
  1864.             class Meta:
    
  1865.                 app_label = "schema"
    
  1866.                 apps = new_apps
    
  1867.                 db_table = "INTEGERPK"
    
  1868. 
    
  1869.         # model requires a new PK
    
  1870.         old_field = Transitional._meta.get_field("j")
    
  1871.         new_field = IntegerField(primary_key=True)
    
  1872.         new_field.model = Transitional
    
  1873.         new_field.set_attributes_from_name("j")
    
  1874. 
    
  1875.         with connection.schema_editor() as editor:
    
  1876.             editor.alter_field(Transitional, old_field, new_field, strict=True)
    
  1877. 
    
  1878.         # Create a model class representing the updated model.
    
  1879.         class IntegerUnique(Model):
    
  1880.             i = IntegerField(unique=True)
    
  1881.             j = IntegerField(primary_key=True)
    
  1882. 
    
  1883.             class Meta:
    
  1884.                 app_label = "schema"
    
  1885.                 apps = new_apps
    
  1886.                 db_table = "INTEGERPK"
    
  1887. 
    
  1888.         # Ensure unique constraint works.
    
  1889.         IntegerUnique.objects.create(i=1, j=1)
    
  1890.         with self.assertRaises(IntegrityError):
    
  1891.             IntegerUnique.objects.create(i=1, j=2)
    
  1892. 
    
  1893.     def test_rename(self):
    
  1894.         """
    
  1895.         Tests simple altering of fields
    
  1896.         """
    
  1897.         # Create the table
    
  1898.         with connection.schema_editor() as editor:
    
  1899.             editor.create_model(Author)
    
  1900.         # Ensure the field is right to begin with
    
  1901.         columns = self.column_classes(Author)
    
  1902.         self.assertEqual(
    
  1903.             columns["name"][0],
    
  1904.             connection.features.introspected_field_types["CharField"],
    
  1905.         )
    
  1906.         self.assertNotIn("display_name", columns)
    
  1907.         # Alter the name field's name
    
  1908.         old_field = Author._meta.get_field("name")
    
  1909.         new_field = CharField(max_length=254)
    
  1910.         new_field.set_attributes_from_name("display_name")
    
  1911.         with connection.schema_editor() as editor:
    
  1912.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  1913.         columns = self.column_classes(Author)
    
  1914.         self.assertEqual(
    
  1915.             columns["display_name"][0],
    
  1916.             connection.features.introspected_field_types["CharField"],
    
  1917.         )
    
  1918.         self.assertNotIn("name", columns)
    
  1919. 
    
  1920.     @isolate_apps("schema")
    
  1921.     def test_rename_referenced_field(self):
    
  1922.         class Author(Model):
    
  1923.             name = CharField(max_length=255, unique=True)
    
  1924. 
    
  1925.             class Meta:
    
  1926.                 app_label = "schema"
    
  1927. 
    
  1928.         class Book(Model):
    
  1929.             author = ForeignKey(Author, CASCADE, to_field="name")
    
  1930. 
    
  1931.             class Meta:
    
  1932.                 app_label = "schema"
    
  1933. 
    
  1934.         with connection.schema_editor() as editor:
    
  1935.             editor.create_model(Author)
    
  1936.             editor.create_model(Book)
    
  1937.         new_field = CharField(max_length=255, unique=True)
    
  1938.         new_field.set_attributes_from_name("renamed")
    
  1939.         with connection.schema_editor(
    
  1940.             atomic=connection.features.supports_atomic_references_rename
    
  1941.         ) as editor:
    
  1942.             editor.alter_field(Author, Author._meta.get_field("name"), new_field)
    
  1943.         # Ensure the foreign key reference was updated.
    
  1944.         self.assertForeignKeyExists(Book, "author_id", "schema_author", "renamed")
    
  1945. 
    
  1946.     @skipIfDBFeature("interprets_empty_strings_as_nulls")
    
  1947.     def test_rename_keep_null_status(self):
    
  1948.         """
    
  1949.         Renaming a field shouldn't affect the not null status.
    
  1950.         """
    
  1951.         with connection.schema_editor() as editor:
    
  1952.             editor.create_model(Note)
    
  1953.         with self.assertRaises(IntegrityError):
    
  1954.             Note.objects.create(info=None)
    
  1955.         old_field = Note._meta.get_field("info")
    
  1956.         new_field = TextField()
    
  1957.         new_field.set_attributes_from_name("detail_info")
    
  1958.         with connection.schema_editor() as editor:
    
  1959.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  1960.         columns = self.column_classes(Note)
    
  1961.         self.assertEqual(columns["detail_info"][0], "TextField")
    
  1962.         self.assertNotIn("info", columns)
    
  1963.         with self.assertRaises(IntegrityError):
    
  1964.             NoteRename.objects.create(detail_info=None)
    
  1965. 
    
  1966.     def _test_m2m_create(self, M2MFieldClass):
    
  1967.         """
    
  1968.         Tests M2M fields on models during creation
    
  1969.         """
    
  1970. 
    
  1971.         class LocalBookWithM2M(Model):
    
  1972.             author = ForeignKey(Author, CASCADE)
    
  1973.             title = CharField(max_length=100, db_index=True)
    
  1974.             pub_date = DateTimeField()
    
  1975.             tags = M2MFieldClass("TagM2MTest", related_name="books")
    
  1976. 
    
  1977.             class Meta:
    
  1978.                 app_label = "schema"
    
  1979.                 apps = new_apps
    
  1980. 
    
  1981.         self.local_models = [LocalBookWithM2M]
    
  1982.         # Create the tables
    
  1983.         with connection.schema_editor() as editor:
    
  1984.             editor.create_model(Author)
    
  1985.             editor.create_model(TagM2MTest)
    
  1986.             editor.create_model(LocalBookWithM2M)
    
  1987.         # Ensure there is now an m2m table there
    
  1988.         columns = self.column_classes(
    
  1989.             LocalBookWithM2M._meta.get_field("tags").remote_field.through
    
  1990.         )
    
  1991.         self.assertEqual(
    
  1992.             columns["tagm2mtest_id"][0],
    
  1993.             connection.features.introspected_field_types["IntegerField"],
    
  1994.         )
    
  1995. 
    
  1996.     def test_m2m_create(self):
    
  1997.         self._test_m2m_create(ManyToManyField)
    
  1998. 
    
  1999.     def test_m2m_create_custom(self):
    
  2000.         self._test_m2m_create(CustomManyToManyField)
    
  2001. 
    
  2002.     def test_m2m_create_inherited(self):
    
  2003.         self._test_m2m_create(InheritedManyToManyField)
    
  2004. 
    
  2005.     def _test_m2m_create_through(self, M2MFieldClass):
    
  2006.         """
    
  2007.         Tests M2M fields on models during creation with through models
    
  2008.         """
    
  2009. 
    
  2010.         class LocalTagThrough(Model):
    
  2011.             book = ForeignKey("schema.LocalBookWithM2MThrough", CASCADE)
    
  2012.             tag = ForeignKey("schema.TagM2MTest", CASCADE)
    
  2013. 
    
  2014.             class Meta:
    
  2015.                 app_label = "schema"
    
  2016.                 apps = new_apps
    
  2017. 
    
  2018.         class LocalBookWithM2MThrough(Model):
    
  2019.             tags = M2MFieldClass(
    
  2020.                 "TagM2MTest", related_name="books", through=LocalTagThrough
    
  2021.             )
    
  2022. 
    
  2023.             class Meta:
    
  2024.                 app_label = "schema"
    
  2025.                 apps = new_apps
    
  2026. 
    
  2027.         self.local_models = [LocalTagThrough, LocalBookWithM2MThrough]
    
  2028. 
    
  2029.         # Create the tables
    
  2030.         with connection.schema_editor() as editor:
    
  2031.             editor.create_model(LocalTagThrough)
    
  2032.             editor.create_model(TagM2MTest)
    
  2033.             editor.create_model(LocalBookWithM2MThrough)
    
  2034.         # Ensure there is now an m2m table there
    
  2035.         columns = self.column_classes(LocalTagThrough)
    
  2036.         self.assertEqual(
    
  2037.             columns["book_id"][0],
    
  2038.             connection.features.introspected_field_types["IntegerField"],
    
  2039.         )
    
  2040.         self.assertEqual(
    
  2041.             columns["tag_id"][0],
    
  2042.             connection.features.introspected_field_types["IntegerField"],
    
  2043.         )
    
  2044. 
    
  2045.     def test_m2m_create_through(self):
    
  2046.         self._test_m2m_create_through(ManyToManyField)
    
  2047. 
    
  2048.     def test_m2m_create_through_custom(self):
    
  2049.         self._test_m2m_create_through(CustomManyToManyField)
    
  2050. 
    
  2051.     def test_m2m_create_through_inherited(self):
    
  2052.         self._test_m2m_create_through(InheritedManyToManyField)
    
  2053. 
    
  2054.     def test_m2m_through_remove(self):
    
  2055.         class LocalAuthorNoteThrough(Model):
    
  2056.             book = ForeignKey("schema.Author", CASCADE)
    
  2057.             tag = ForeignKey("self", CASCADE)
    
  2058. 
    
  2059.             class Meta:
    
  2060.                 app_label = "schema"
    
  2061.                 apps = new_apps
    
  2062. 
    
  2063.         class LocalNoteWithM2MThrough(Model):
    
  2064.             authors = ManyToManyField("schema.Author", through=LocalAuthorNoteThrough)
    
  2065. 
    
  2066.             class Meta:
    
  2067.                 app_label = "schema"
    
  2068.                 apps = new_apps
    
  2069. 
    
  2070.         self.local_models = [LocalAuthorNoteThrough, LocalNoteWithM2MThrough]
    
  2071.         # Create the tables.
    
  2072.         with connection.schema_editor() as editor:
    
  2073.             editor.create_model(Author)
    
  2074.             editor.create_model(LocalAuthorNoteThrough)
    
  2075.             editor.create_model(LocalNoteWithM2MThrough)
    
  2076.         # Remove the through parameter.
    
  2077.         old_field = LocalNoteWithM2MThrough._meta.get_field("authors")
    
  2078.         new_field = ManyToManyField("Author")
    
  2079.         new_field.set_attributes_from_name("authors")
    
  2080.         msg = (
    
  2081.             f"Cannot alter field {old_field} into {new_field} - they are not "
    
  2082.             f"compatible types (you cannot alter to or from M2M fields, or add or "
    
  2083.             f"remove through= on M2M fields)"
    
  2084.         )
    
  2085.         with connection.schema_editor() as editor:
    
  2086.             with self.assertRaisesMessage(ValueError, msg):
    
  2087.                 editor.alter_field(LocalNoteWithM2MThrough, old_field, new_field)
    
  2088. 
    
  2089.     def _test_m2m(self, M2MFieldClass):
    
  2090.         """
    
  2091.         Tests adding/removing M2M fields on models
    
  2092.         """
    
  2093. 
    
  2094.         class LocalAuthorWithM2M(Model):
    
  2095.             name = CharField(max_length=255)
    
  2096. 
    
  2097.             class Meta:
    
  2098.                 app_label = "schema"
    
  2099.                 apps = new_apps
    
  2100. 
    
  2101.         self.local_models = [LocalAuthorWithM2M]
    
  2102. 
    
  2103.         # Create the tables
    
  2104.         with connection.schema_editor() as editor:
    
  2105.             editor.create_model(LocalAuthorWithM2M)
    
  2106.             editor.create_model(TagM2MTest)
    
  2107.         # Create an M2M field
    
  2108.         new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors")
    
  2109.         new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
    
  2110.         # Ensure there's no m2m table there
    
  2111.         with self.assertRaises(DatabaseError):
    
  2112.             self.column_classes(new_field.remote_field.through)
    
  2113.         # Add the field
    
  2114.         with CaptureQueriesContext(
    
  2115.             connection
    
  2116.         ) as ctx, connection.schema_editor() as editor:
    
  2117.             editor.add_field(LocalAuthorWithM2M, new_field)
    
  2118.         # Table is not rebuilt.
    
  2119.         self.assertEqual(
    
  2120.             len(
    
  2121.                 [
    
  2122.                     query["sql"]
    
  2123.                     for query in ctx.captured_queries
    
  2124.                     if "CREATE TABLE" in query["sql"]
    
  2125.                 ]
    
  2126.             ),
    
  2127.             1,
    
  2128.         )
    
  2129.         self.assertIs(
    
  2130.             any("DROP TABLE" in query["sql"] for query in ctx.captured_queries),
    
  2131.             False,
    
  2132.         )
    
  2133.         # Ensure there is now an m2m table there
    
  2134.         columns = self.column_classes(new_field.remote_field.through)
    
  2135.         self.assertEqual(
    
  2136.             columns["tagm2mtest_id"][0],
    
  2137.             connection.features.introspected_field_types["IntegerField"],
    
  2138.         )
    
  2139. 
    
  2140.         # "Alter" the field. This should not rename the DB table to itself.
    
  2141.         with connection.schema_editor() as editor:
    
  2142.             editor.alter_field(LocalAuthorWithM2M, new_field, new_field, strict=True)
    
  2143. 
    
  2144.         # Remove the M2M table again
    
  2145.         with connection.schema_editor() as editor:
    
  2146.             editor.remove_field(LocalAuthorWithM2M, new_field)
    
  2147.         # Ensure there's no m2m table there
    
  2148.         with self.assertRaises(DatabaseError):
    
  2149.             self.column_classes(new_field.remote_field.through)
    
  2150. 
    
  2151.         # Make sure the model state is coherent with the table one now that
    
  2152.         # we've removed the tags field.
    
  2153.         opts = LocalAuthorWithM2M._meta
    
  2154.         opts.local_many_to_many.remove(new_field)
    
  2155.         del new_apps.all_models["schema"][
    
  2156.             new_field.remote_field.through._meta.model_name
    
  2157.         ]
    
  2158.         opts._expire_cache()
    
  2159. 
    
  2160.     def test_m2m(self):
    
  2161.         self._test_m2m(ManyToManyField)
    
  2162. 
    
  2163.     def test_m2m_custom(self):
    
  2164.         self._test_m2m(CustomManyToManyField)
    
  2165. 
    
  2166.     def test_m2m_inherited(self):
    
  2167.         self._test_m2m(InheritedManyToManyField)
    
  2168. 
    
  2169.     def _test_m2m_through_alter(self, M2MFieldClass):
    
  2170.         """
    
  2171.         Tests altering M2Ms with explicit through models (should no-op)
    
  2172.         """
    
  2173. 
    
  2174.         class LocalAuthorTag(Model):
    
  2175.             author = ForeignKey("schema.LocalAuthorWithM2MThrough", CASCADE)
    
  2176.             tag = ForeignKey("schema.TagM2MTest", CASCADE)
    
  2177. 
    
  2178.             class Meta:
    
  2179.                 app_label = "schema"
    
  2180.                 apps = new_apps
    
  2181. 
    
  2182.         class LocalAuthorWithM2MThrough(Model):
    
  2183.             name = CharField(max_length=255)
    
  2184.             tags = M2MFieldClass(
    
  2185.                 "schema.TagM2MTest", related_name="authors", through=LocalAuthorTag
    
  2186.             )
    
  2187. 
    
  2188.             class Meta:
    
  2189.                 app_label = "schema"
    
  2190.                 apps = new_apps
    
  2191. 
    
  2192.         self.local_models = [LocalAuthorTag, LocalAuthorWithM2MThrough]
    
  2193. 
    
  2194.         # Create the tables
    
  2195.         with connection.schema_editor() as editor:
    
  2196.             editor.create_model(LocalAuthorTag)
    
  2197.             editor.create_model(LocalAuthorWithM2MThrough)
    
  2198.             editor.create_model(TagM2MTest)
    
  2199.         # Ensure the m2m table is there
    
  2200.         self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
    
  2201.         # "Alter" the field's blankness. This should not actually do anything.
    
  2202.         old_field = LocalAuthorWithM2MThrough._meta.get_field("tags")
    
  2203.         new_field = M2MFieldClass(
    
  2204.             "schema.TagM2MTest", related_name="authors", through=LocalAuthorTag
    
  2205.         )
    
  2206.         new_field.contribute_to_class(LocalAuthorWithM2MThrough, "tags")
    
  2207.         with connection.schema_editor() as editor:
    
  2208.             editor.alter_field(
    
  2209.                 LocalAuthorWithM2MThrough, old_field, new_field, strict=True
    
  2210.             )
    
  2211.         # Ensure the m2m table is still there
    
  2212.         self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
    
  2213. 
    
  2214.     def test_m2m_through_alter(self):
    
  2215.         self._test_m2m_through_alter(ManyToManyField)
    
  2216. 
    
  2217.     def test_m2m_through_alter_custom(self):
    
  2218.         self._test_m2m_through_alter(CustomManyToManyField)
    
  2219. 
    
  2220.     def test_m2m_through_alter_inherited(self):
    
  2221.         self._test_m2m_through_alter(InheritedManyToManyField)
    
  2222. 
    
  2223.     def _test_m2m_repoint(self, M2MFieldClass):
    
  2224.         """
    
  2225.         Tests repointing M2M fields
    
  2226.         """
    
  2227. 
    
  2228.         class LocalBookWithM2M(Model):
    
  2229.             author = ForeignKey(Author, CASCADE)
    
  2230.             title = CharField(max_length=100, db_index=True)
    
  2231.             pub_date = DateTimeField()
    
  2232.             tags = M2MFieldClass("TagM2MTest", related_name="books")
    
  2233. 
    
  2234.             class Meta:
    
  2235.                 app_label = "schema"
    
  2236.                 apps = new_apps
    
  2237. 
    
  2238.         self.local_models = [LocalBookWithM2M]
    
  2239.         # Create the tables
    
  2240.         with connection.schema_editor() as editor:
    
  2241.             editor.create_model(Author)
    
  2242.             editor.create_model(LocalBookWithM2M)
    
  2243.             editor.create_model(TagM2MTest)
    
  2244.             editor.create_model(UniqueTest)
    
  2245.         # Ensure the M2M exists and points to TagM2MTest
    
  2246.         if connection.features.supports_foreign_keys:
    
  2247.             self.assertForeignKeyExists(
    
  2248.                 LocalBookWithM2M._meta.get_field("tags").remote_field.through,
    
  2249.                 "tagm2mtest_id",
    
  2250.                 "schema_tagm2mtest",
    
  2251.             )
    
  2252.         # Repoint the M2M
    
  2253.         old_field = LocalBookWithM2M._meta.get_field("tags")
    
  2254.         new_field = M2MFieldClass(UniqueTest)
    
  2255.         new_field.contribute_to_class(LocalBookWithM2M, "uniques")
    
  2256.         with connection.schema_editor() as editor:
    
  2257.             editor.alter_field(LocalBookWithM2M, old_field, new_field, strict=True)
    
  2258.         # Ensure old M2M is gone
    
  2259.         with self.assertRaises(DatabaseError):
    
  2260.             self.column_classes(
    
  2261.                 LocalBookWithM2M._meta.get_field("tags").remote_field.through
    
  2262.             )
    
  2263. 
    
  2264.         # This model looks like the new model and is used for teardown.
    
  2265.         opts = LocalBookWithM2M._meta
    
  2266.         opts.local_many_to_many.remove(old_field)
    
  2267.         # Ensure the new M2M exists and points to UniqueTest
    
  2268.         if connection.features.supports_foreign_keys:
    
  2269.             self.assertForeignKeyExists(
    
  2270.                 new_field.remote_field.through, "uniquetest_id", "schema_uniquetest"
    
  2271.             )
    
  2272. 
    
  2273.     def test_m2m_repoint(self):
    
  2274.         self._test_m2m_repoint(ManyToManyField)
    
  2275. 
    
  2276.     def test_m2m_repoint_custom(self):
    
  2277.         self._test_m2m_repoint(CustomManyToManyField)
    
  2278. 
    
  2279.     def test_m2m_repoint_inherited(self):
    
  2280.         self._test_m2m_repoint(InheritedManyToManyField)
    
  2281. 
    
  2282.     @isolate_apps("schema")
    
  2283.     def test_m2m_rename_field_in_target_model(self):
    
  2284.         class LocalTagM2MTest(Model):
    
  2285.             title = CharField(max_length=255)
    
  2286. 
    
  2287.             class Meta:
    
  2288.                 app_label = "schema"
    
  2289. 
    
  2290.         class LocalM2M(Model):
    
  2291.             tags = ManyToManyField(LocalTagM2MTest)
    
  2292. 
    
  2293.             class Meta:
    
  2294.                 app_label = "schema"
    
  2295. 
    
  2296.         # Create the tables.
    
  2297.         with connection.schema_editor() as editor:
    
  2298.             editor.create_model(LocalM2M)
    
  2299.             editor.create_model(LocalTagM2MTest)
    
  2300.         self.isolated_local_models = [LocalM2M, LocalTagM2MTest]
    
  2301.         # Ensure the m2m table is there.
    
  2302.         self.assertEqual(len(self.column_classes(LocalM2M)), 1)
    
  2303.         # Alter a field in LocalTagM2MTest.
    
  2304.         old_field = LocalTagM2MTest._meta.get_field("title")
    
  2305.         new_field = CharField(max_length=254)
    
  2306.         new_field.contribute_to_class(LocalTagM2MTest, "title1")
    
  2307.         # @isolate_apps() and inner models are needed to have the model
    
  2308.         # relations populated, otherwise this doesn't act as a regression test.
    
  2309.         self.assertEqual(len(new_field.model._meta.related_objects), 1)
    
  2310.         with connection.schema_editor() as editor:
    
  2311.             editor.alter_field(LocalTagM2MTest, old_field, new_field, strict=True)
    
  2312.         # Ensure the m2m table is still there.
    
  2313.         self.assertEqual(len(self.column_classes(LocalM2M)), 1)
    
  2314. 
    
  2315.     @skipUnlessDBFeature(
    
  2316.         "supports_column_check_constraints", "can_introspect_check_constraints"
    
  2317.     )
    
  2318.     def test_check_constraints(self):
    
  2319.         """
    
  2320.         Tests creating/deleting CHECK constraints
    
  2321.         """
    
  2322.         # Create the tables
    
  2323.         with connection.schema_editor() as editor:
    
  2324.             editor.create_model(Author)
    
  2325.         # Ensure the constraint exists
    
  2326.         constraints = self.get_constraints(Author._meta.db_table)
    
  2327.         if not any(
    
  2328.             details["columns"] == ["height"] and details["check"]
    
  2329.             for details in constraints.values()
    
  2330.         ):
    
  2331.             self.fail("No check constraint for height found")
    
  2332.         # Alter the column to remove it
    
  2333.         old_field = Author._meta.get_field("height")
    
  2334.         new_field = IntegerField(null=True, blank=True)
    
  2335.         new_field.set_attributes_from_name("height")
    
  2336.         with connection.schema_editor() as editor:
    
  2337.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  2338.         constraints = self.get_constraints(Author._meta.db_table)
    
  2339.         for details in constraints.values():
    
  2340.             if details["columns"] == ["height"] and details["check"]:
    
  2341.                 self.fail("Check constraint for height found")
    
  2342.         # Alter the column to re-add it
    
  2343.         new_field2 = Author._meta.get_field("height")
    
  2344.         with connection.schema_editor() as editor:
    
  2345.             editor.alter_field(Author, new_field, new_field2, strict=True)
    
  2346.         constraints = self.get_constraints(Author._meta.db_table)
    
  2347.         if not any(
    
  2348.             details["columns"] == ["height"] and details["check"]
    
  2349.             for details in constraints.values()
    
  2350.         ):
    
  2351.             self.fail("No check constraint for height found")
    
  2352. 
    
  2353.     @skipUnlessDBFeature(
    
  2354.         "supports_column_check_constraints", "can_introspect_check_constraints"
    
  2355.     )
    
  2356.     @isolate_apps("schema")
    
  2357.     def test_check_constraint_timedelta_param(self):
    
  2358.         class DurationModel(Model):
    
  2359.             duration = DurationField()
    
  2360. 
    
  2361.             class Meta:
    
  2362.                 app_label = "schema"
    
  2363. 
    
  2364.         with connection.schema_editor() as editor:
    
  2365.             editor.create_model(DurationModel)
    
  2366.         self.isolated_local_models = [DurationModel]
    
  2367.         constraint_name = "duration_gte_5_minutes"
    
  2368.         constraint = CheckConstraint(
    
  2369.             check=Q(duration__gt=datetime.timedelta(minutes=5)),
    
  2370.             name=constraint_name,
    
  2371.         )
    
  2372.         DurationModel._meta.constraints = [constraint]
    
  2373.         with connection.schema_editor() as editor:
    
  2374.             editor.add_constraint(DurationModel, constraint)
    
  2375.         constraints = self.get_constraints(DurationModel._meta.db_table)
    
  2376.         self.assertIn(constraint_name, constraints)
    
  2377.         with self.assertRaises(IntegrityError), atomic():
    
  2378.             DurationModel.objects.create(duration=datetime.timedelta(minutes=4))
    
  2379.         DurationModel.objects.create(duration=datetime.timedelta(minutes=10))
    
  2380. 
    
  2381.     @skipUnlessDBFeature(
    
  2382.         "supports_column_check_constraints", "can_introspect_check_constraints"
    
  2383.     )
    
  2384.     def test_remove_field_check_does_not_remove_meta_constraints(self):
    
  2385.         with connection.schema_editor() as editor:
    
  2386.             editor.create_model(Author)
    
  2387.         # Add the custom check constraint
    
  2388.         constraint = CheckConstraint(
    
  2389.             check=Q(height__gte=0), name="author_height_gte_0_check"
    
  2390.         )
    
  2391.         custom_constraint_name = constraint.name
    
  2392.         Author._meta.constraints = [constraint]
    
  2393.         with connection.schema_editor() as editor:
    
  2394.             editor.add_constraint(Author, constraint)
    
  2395.         # Ensure the constraints exist
    
  2396.         constraints = self.get_constraints(Author._meta.db_table)
    
  2397.         self.assertIn(custom_constraint_name, constraints)
    
  2398.         other_constraints = [
    
  2399.             name
    
  2400.             for name, details in constraints.items()
    
  2401.             if details["columns"] == ["height"]
    
  2402.             and details["check"]
    
  2403.             and name != custom_constraint_name
    
  2404.         ]
    
  2405.         self.assertEqual(len(other_constraints), 1)
    
  2406.         # Alter the column to remove field check
    
  2407.         old_field = Author._meta.get_field("height")
    
  2408.         new_field = IntegerField(null=True, blank=True)
    
  2409.         new_field.set_attributes_from_name("height")
    
  2410.         with connection.schema_editor() as editor:
    
  2411.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  2412.         constraints = self.get_constraints(Author._meta.db_table)
    
  2413.         self.assertIn(custom_constraint_name, constraints)
    
  2414.         other_constraints = [
    
  2415.             name
    
  2416.             for name, details in constraints.items()
    
  2417.             if details["columns"] == ["height"]
    
  2418.             and details["check"]
    
  2419.             and name != custom_constraint_name
    
  2420.         ]
    
  2421.         self.assertEqual(len(other_constraints), 0)
    
  2422.         # Alter the column to re-add field check
    
  2423.         new_field2 = Author._meta.get_field("height")
    
  2424.         with connection.schema_editor() as editor:
    
  2425.             editor.alter_field(Author, new_field, new_field2, strict=True)
    
  2426.         constraints = self.get_constraints(Author._meta.db_table)
    
  2427.         self.assertIn(custom_constraint_name, constraints)
    
  2428.         other_constraints = [
    
  2429.             name
    
  2430.             for name, details in constraints.items()
    
  2431.             if details["columns"] == ["height"]
    
  2432.             and details["check"]
    
  2433.             and name != custom_constraint_name
    
  2434.         ]
    
  2435.         self.assertEqual(len(other_constraints), 1)
    
  2436.         # Drop the check constraint
    
  2437.         with connection.schema_editor() as editor:
    
  2438.             Author._meta.constraints = []
    
  2439.             editor.remove_constraint(Author, constraint)
    
  2440. 
    
  2441.     def test_unique(self):
    
  2442.         """
    
  2443.         Tests removing and adding unique constraints to a single column.
    
  2444.         """
    
  2445.         # Create the table
    
  2446.         with connection.schema_editor() as editor:
    
  2447.             editor.create_model(Tag)
    
  2448.         # Ensure the field is unique to begin with
    
  2449.         Tag.objects.create(title="foo", slug="foo")
    
  2450.         with self.assertRaises(IntegrityError):
    
  2451.             Tag.objects.create(title="bar", slug="foo")
    
  2452.         Tag.objects.all().delete()
    
  2453.         # Alter the slug field to be non-unique
    
  2454.         old_field = Tag._meta.get_field("slug")
    
  2455.         new_field = SlugField(unique=False)
    
  2456.         new_field.set_attributes_from_name("slug")
    
  2457.         with connection.schema_editor() as editor:
    
  2458.             editor.alter_field(Tag, old_field, new_field, strict=True)
    
  2459.         # Ensure the field is no longer unique
    
  2460.         Tag.objects.create(title="foo", slug="foo")
    
  2461.         Tag.objects.create(title="bar", slug="foo")
    
  2462.         Tag.objects.all().delete()
    
  2463.         # Alter the slug field to be unique
    
  2464.         new_field2 = SlugField(unique=True)
    
  2465.         new_field2.set_attributes_from_name("slug")
    
  2466.         with connection.schema_editor() as editor:
    
  2467.             editor.alter_field(Tag, new_field, new_field2, strict=True)
    
  2468.         # Ensure the field is unique again
    
  2469.         Tag.objects.create(title="foo", slug="foo")
    
  2470.         with self.assertRaises(IntegrityError):
    
  2471.             Tag.objects.create(title="bar", slug="foo")
    
  2472.         Tag.objects.all().delete()
    
  2473.         # Rename the field
    
  2474.         new_field3 = SlugField(unique=True)
    
  2475.         new_field3.set_attributes_from_name("slug2")
    
  2476.         with connection.schema_editor() as editor:
    
  2477.             editor.alter_field(Tag, new_field2, new_field3, strict=True)
    
  2478.         # Ensure the field is still unique
    
  2479.         TagUniqueRename.objects.create(title="foo", slug2="foo")
    
  2480.         with self.assertRaises(IntegrityError):
    
  2481.             TagUniqueRename.objects.create(title="bar", slug2="foo")
    
  2482.         Tag.objects.all().delete()
    
  2483. 
    
  2484.     def test_unique_name_quoting(self):
    
  2485.         old_table_name = TagUniqueRename._meta.db_table
    
  2486.         try:
    
  2487.             with connection.schema_editor() as editor:
    
  2488.                 editor.create_model(TagUniqueRename)
    
  2489.                 editor.alter_db_table(TagUniqueRename, old_table_name, "unique-table")
    
  2490.                 TagUniqueRename._meta.db_table = "unique-table"
    
  2491.                 # This fails if the unique index name isn't quoted.
    
  2492.                 editor.alter_unique_together(TagUniqueRename, [], (("title", "slug2"),))
    
  2493.         finally:
    
  2494.             with connection.schema_editor() as editor:
    
  2495.                 editor.delete_model(TagUniqueRename)
    
  2496.             TagUniqueRename._meta.db_table = old_table_name
    
  2497. 
    
  2498.     @isolate_apps("schema")
    
  2499.     @skipUnlessDBFeature("supports_foreign_keys")
    
  2500.     def test_unique_no_unnecessary_fk_drops(self):
    
  2501.         """
    
  2502.         If AlterField isn't selective about dropping foreign key constraints
    
  2503.         when modifying a field with a unique constraint, the AlterField
    
  2504.         incorrectly drops and recreates the Book.author foreign key even though
    
  2505.         it doesn't restrict the field being changed (#29193).
    
  2506.         """
    
  2507. 
    
  2508.         class Author(Model):
    
  2509.             name = CharField(max_length=254, unique=True)
    
  2510. 
    
  2511.             class Meta:
    
  2512.                 app_label = "schema"
    
  2513. 
    
  2514.         class Book(Model):
    
  2515.             author = ForeignKey(Author, CASCADE)
    
  2516. 
    
  2517.             class Meta:
    
  2518.                 app_label = "schema"
    
  2519. 
    
  2520.         with connection.schema_editor() as editor:
    
  2521.             editor.create_model(Author)
    
  2522.             editor.create_model(Book)
    
  2523.         new_field = CharField(max_length=255, unique=True)
    
  2524.         new_field.model = Author
    
  2525.         new_field.set_attributes_from_name("name")
    
  2526.         with self.assertLogs("django.db.backends.schema", "DEBUG") as cm:
    
  2527.             with connection.schema_editor() as editor:
    
  2528.                 editor.alter_field(Author, Author._meta.get_field("name"), new_field)
    
  2529.         # One SQL statement is executed to alter the field.
    
  2530.         self.assertEqual(len(cm.records), 1)
    
  2531. 
    
  2532.     @isolate_apps("schema")
    
  2533.     def test_unique_and_reverse_m2m(self):
    
  2534.         """
    
  2535.         AlterField can modify a unique field when there's a reverse M2M
    
  2536.         relation on the model.
    
  2537.         """
    
  2538. 
    
  2539.         class Tag(Model):
    
  2540.             title = CharField(max_length=255)
    
  2541.             slug = SlugField(unique=True)
    
  2542. 
    
  2543.             class Meta:
    
  2544.                 app_label = "schema"
    
  2545. 
    
  2546.         class Book(Model):
    
  2547.             tags = ManyToManyField(Tag, related_name="books")
    
  2548. 
    
  2549.             class Meta:
    
  2550.                 app_label = "schema"
    
  2551. 
    
  2552.         self.isolated_local_models = [Book._meta.get_field("tags").remote_field.through]
    
  2553.         with connection.schema_editor() as editor:
    
  2554.             editor.create_model(Tag)
    
  2555.             editor.create_model(Book)
    
  2556.         new_field = SlugField(max_length=75, unique=True)
    
  2557.         new_field.model = Tag
    
  2558.         new_field.set_attributes_from_name("slug")
    
  2559.         with self.assertLogs("django.db.backends.schema", "DEBUG") as cm:
    
  2560.             with connection.schema_editor() as editor:
    
  2561.                 editor.alter_field(Tag, Tag._meta.get_field("slug"), new_field)
    
  2562.         # One SQL statement is executed to alter the field.
    
  2563.         self.assertEqual(len(cm.records), 1)
    
  2564.         # Ensure that the field is still unique.
    
  2565.         Tag.objects.create(title="foo", slug="foo")
    
  2566.         with self.assertRaises(IntegrityError):
    
  2567.             Tag.objects.create(title="bar", slug="foo")
    
  2568. 
    
  2569.     @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
    
  2570.     def test_remove_field_unique_does_not_remove_meta_constraints(self):
    
  2571.         with connection.schema_editor() as editor:
    
  2572.             editor.create_model(AuthorWithUniqueName)
    
  2573.         self.local_models = [AuthorWithUniqueName]
    
  2574.         # Add the custom unique constraint
    
  2575.         constraint = UniqueConstraint(fields=["name"], name="author_name_uniq")
    
  2576.         custom_constraint_name = constraint.name
    
  2577.         AuthorWithUniqueName._meta.constraints = [constraint]
    
  2578.         with connection.schema_editor() as editor:
    
  2579.             editor.add_constraint(AuthorWithUniqueName, constraint)
    
  2580.         # Ensure the constraints exist
    
  2581.         constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
    
  2582.         self.assertIn(custom_constraint_name, constraints)
    
  2583.         other_constraints = [
    
  2584.             name
    
  2585.             for name, details in constraints.items()
    
  2586.             if details["columns"] == ["name"]
    
  2587.             and details["unique"]
    
  2588.             and name != custom_constraint_name
    
  2589.         ]
    
  2590.         self.assertEqual(len(other_constraints), 1)
    
  2591.         # Alter the column to remove field uniqueness
    
  2592.         old_field = AuthorWithUniqueName._meta.get_field("name")
    
  2593.         new_field = CharField(max_length=255)
    
  2594.         new_field.set_attributes_from_name("name")
    
  2595.         with connection.schema_editor() as editor:
    
  2596.             editor.alter_field(AuthorWithUniqueName, old_field, new_field, strict=True)
    
  2597.         constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
    
  2598.         self.assertIn(custom_constraint_name, constraints)
    
  2599.         other_constraints = [
    
  2600.             name
    
  2601.             for name, details in constraints.items()
    
  2602.             if details["columns"] == ["name"]
    
  2603.             and details["unique"]
    
  2604.             and name != custom_constraint_name
    
  2605.         ]
    
  2606.         self.assertEqual(len(other_constraints), 0)
    
  2607.         # Alter the column to re-add field uniqueness
    
  2608.         new_field2 = AuthorWithUniqueName._meta.get_field("name")
    
  2609.         with connection.schema_editor() as editor:
    
  2610.             editor.alter_field(AuthorWithUniqueName, new_field, new_field2, strict=True)
    
  2611.         constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
    
  2612.         self.assertIn(custom_constraint_name, constraints)
    
  2613.         other_constraints = [
    
  2614.             name
    
  2615.             for name, details in constraints.items()
    
  2616.             if details["columns"] == ["name"]
    
  2617.             and details["unique"]
    
  2618.             and name != custom_constraint_name
    
  2619.         ]
    
  2620.         self.assertEqual(len(other_constraints), 1)
    
  2621.         # Drop the unique constraint
    
  2622.         with connection.schema_editor() as editor:
    
  2623.             AuthorWithUniqueName._meta.constraints = []
    
  2624.             editor.remove_constraint(AuthorWithUniqueName, constraint)
    
  2625. 
    
  2626.     def test_unique_together(self):
    
  2627.         """
    
  2628.         Tests removing and adding unique_together constraints on a model.
    
  2629.         """
    
  2630.         # Create the table
    
  2631.         with connection.schema_editor() as editor:
    
  2632.             editor.create_model(UniqueTest)
    
  2633.         # Ensure the fields are unique to begin with
    
  2634.         UniqueTest.objects.create(year=2012, slug="foo")
    
  2635.         UniqueTest.objects.create(year=2011, slug="foo")
    
  2636.         UniqueTest.objects.create(year=2011, slug="bar")
    
  2637.         with self.assertRaises(IntegrityError):
    
  2638.             UniqueTest.objects.create(year=2012, slug="foo")
    
  2639.         UniqueTest.objects.all().delete()
    
  2640.         # Alter the model to its non-unique-together companion
    
  2641.         with connection.schema_editor() as editor:
    
  2642.             editor.alter_unique_together(
    
  2643.                 UniqueTest, UniqueTest._meta.unique_together, []
    
  2644.             )
    
  2645.         # Ensure the fields are no longer unique
    
  2646.         UniqueTest.objects.create(year=2012, slug="foo")
    
  2647.         UniqueTest.objects.create(year=2012, slug="foo")
    
  2648.         UniqueTest.objects.all().delete()
    
  2649.         # Alter it back
    
  2650.         new_field2 = SlugField(unique=True)
    
  2651.         new_field2.set_attributes_from_name("slug")
    
  2652.         with connection.schema_editor() as editor:
    
  2653.             editor.alter_unique_together(
    
  2654.                 UniqueTest, [], UniqueTest._meta.unique_together
    
  2655.             )
    
  2656.         # Ensure the fields are unique again
    
  2657.         UniqueTest.objects.create(year=2012, slug="foo")
    
  2658.         with self.assertRaises(IntegrityError):
    
  2659.             UniqueTest.objects.create(year=2012, slug="foo")
    
  2660.         UniqueTest.objects.all().delete()
    
  2661. 
    
  2662.     def test_unique_together_with_fk(self):
    
  2663.         """
    
  2664.         Tests removing and adding unique_together constraints that include
    
  2665.         a foreign key.
    
  2666.         """
    
  2667.         # Create the table
    
  2668.         with connection.schema_editor() as editor:
    
  2669.             editor.create_model(Author)
    
  2670.             editor.create_model(Book)
    
  2671.         # Ensure the fields are unique to begin with
    
  2672.         self.assertEqual(Book._meta.unique_together, ())
    
  2673.         # Add the unique_together constraint
    
  2674.         with connection.schema_editor() as editor:
    
  2675.             editor.alter_unique_together(Book, [], [["author", "title"]])
    
  2676.         # Alter it back
    
  2677.         with connection.schema_editor() as editor:
    
  2678.             editor.alter_unique_together(Book, [["author", "title"]], [])
    
  2679. 
    
  2680.     def test_unique_together_with_fk_with_existing_index(self):
    
  2681.         """
    
  2682.         Tests removing and adding unique_together constraints that include
    
  2683.         a foreign key, where the foreign key is added after the model is
    
  2684.         created.
    
  2685.         """
    
  2686.         # Create the tables
    
  2687.         with connection.schema_editor() as editor:
    
  2688.             editor.create_model(Author)
    
  2689.             editor.create_model(BookWithoutAuthor)
    
  2690.             new_field = ForeignKey(Author, CASCADE)
    
  2691.             new_field.set_attributes_from_name("author")
    
  2692.             editor.add_field(BookWithoutAuthor, new_field)
    
  2693.         # Ensure the fields aren't unique to begin with
    
  2694.         self.assertEqual(Book._meta.unique_together, ())
    
  2695.         # Add the unique_together constraint
    
  2696.         with connection.schema_editor() as editor:
    
  2697.             editor.alter_unique_together(Book, [], [["author", "title"]])
    
  2698.         # Alter it back
    
  2699.         with connection.schema_editor() as editor:
    
  2700.             editor.alter_unique_together(Book, [["author", "title"]], [])
    
  2701. 
    
  2702.     @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
    
  2703.     def test_remove_unique_together_does_not_remove_meta_constraints(self):
    
  2704.         with connection.schema_editor() as editor:
    
  2705.             editor.create_model(AuthorWithUniqueNameAndBirthday)
    
  2706.         self.local_models = [AuthorWithUniqueNameAndBirthday]
    
  2707.         # Add the custom unique constraint
    
  2708.         constraint = UniqueConstraint(
    
  2709.             fields=["name", "birthday"], name="author_name_birthday_uniq"
    
  2710.         )
    
  2711.         custom_constraint_name = constraint.name
    
  2712.         AuthorWithUniqueNameAndBirthday._meta.constraints = [constraint]
    
  2713.         with connection.schema_editor() as editor:
    
  2714.             editor.add_constraint(AuthorWithUniqueNameAndBirthday, constraint)
    
  2715.         # Ensure the constraints exist
    
  2716.         constraints = self.get_constraints(
    
  2717.             AuthorWithUniqueNameAndBirthday._meta.db_table
    
  2718.         )
    
  2719.         self.assertIn(custom_constraint_name, constraints)
    
  2720.         other_constraints = [
    
  2721.             name
    
  2722.             for name, details in constraints.items()
    
  2723.             if details["columns"] == ["name", "birthday"]
    
  2724.             and details["unique"]
    
  2725.             and name != custom_constraint_name
    
  2726.         ]
    
  2727.         self.assertEqual(len(other_constraints), 1)
    
  2728.         # Remove unique together
    
  2729.         unique_together = AuthorWithUniqueNameAndBirthday._meta.unique_together
    
  2730.         with connection.schema_editor() as editor:
    
  2731.             editor.alter_unique_together(
    
  2732.                 AuthorWithUniqueNameAndBirthday, unique_together, []
    
  2733.             )
    
  2734.         constraints = self.get_constraints(
    
  2735.             AuthorWithUniqueNameAndBirthday._meta.db_table
    
  2736.         )
    
  2737.         self.assertIn(custom_constraint_name, constraints)
    
  2738.         other_constraints = [
    
  2739.             name
    
  2740.             for name, details in constraints.items()
    
  2741.             if details["columns"] == ["name", "birthday"]
    
  2742.             and details["unique"]
    
  2743.             and name != custom_constraint_name
    
  2744.         ]
    
  2745.         self.assertEqual(len(other_constraints), 0)
    
  2746.         # Re-add unique together
    
  2747.         with connection.schema_editor() as editor:
    
  2748.             editor.alter_unique_together(
    
  2749.                 AuthorWithUniqueNameAndBirthday, [], unique_together
    
  2750.             )
    
  2751.         constraints = self.get_constraints(
    
  2752.             AuthorWithUniqueNameAndBirthday._meta.db_table
    
  2753.         )
    
  2754.         self.assertIn(custom_constraint_name, constraints)
    
  2755.         other_constraints = [
    
  2756.             name
    
  2757.             for name, details in constraints.items()
    
  2758.             if details["columns"] == ["name", "birthday"]
    
  2759.             and details["unique"]
    
  2760.             and name != custom_constraint_name
    
  2761.         ]
    
  2762.         self.assertEqual(len(other_constraints), 1)
    
  2763.         # Drop the unique constraint
    
  2764.         with connection.schema_editor() as editor:
    
  2765.             AuthorWithUniqueNameAndBirthday._meta.constraints = []
    
  2766.             editor.remove_constraint(AuthorWithUniqueNameAndBirthday, constraint)
    
  2767. 
    
  2768.     def test_unique_constraint(self):
    
  2769.         with connection.schema_editor() as editor:
    
  2770.             editor.create_model(Author)
    
  2771.         constraint = UniqueConstraint(fields=["name"], name="name_uq")
    
  2772.         # Add constraint.
    
  2773.         with connection.schema_editor() as editor:
    
  2774.             editor.add_constraint(Author, constraint)
    
  2775.             sql = constraint.create_sql(Author, editor)
    
  2776.         table = Author._meta.db_table
    
  2777.         self.assertIs(sql.references_table(table), True)
    
  2778.         self.assertIs(sql.references_column(table, "name"), True)
    
  2779.         # Remove constraint.
    
  2780.         with connection.schema_editor() as editor:
    
  2781.             editor.remove_constraint(Author, constraint)
    
  2782.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2783. 
    
  2784.     @skipUnlessDBFeature("supports_expression_indexes")
    
  2785.     def test_func_unique_constraint(self):
    
  2786.         with connection.schema_editor() as editor:
    
  2787.             editor.create_model(Author)
    
  2788.         constraint = UniqueConstraint(Upper("name").desc(), name="func_upper_uq")
    
  2789.         # Add constraint.
    
  2790.         with connection.schema_editor() as editor:
    
  2791.             editor.add_constraint(Author, constraint)
    
  2792.             sql = constraint.create_sql(Author, editor)
    
  2793.         table = Author._meta.db_table
    
  2794.         constraints = self.get_constraints(table)
    
  2795.         if connection.features.supports_index_column_ordering:
    
  2796.             self.assertIndexOrder(table, constraint.name, ["DESC"])
    
  2797.         self.assertIn(constraint.name, constraints)
    
  2798.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2799.         # SQL contains a database function.
    
  2800.         self.assertIs(sql.references_column(table, "name"), True)
    
  2801.         self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql))
    
  2802.         # Remove constraint.
    
  2803.         with connection.schema_editor() as editor:
    
  2804.             editor.remove_constraint(Author, constraint)
    
  2805.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2806. 
    
  2807.     @skipUnlessDBFeature("supports_expression_indexes")
    
  2808.     def test_composite_func_unique_constraint(self):
    
  2809.         with connection.schema_editor() as editor:
    
  2810.             editor.create_model(Author)
    
  2811.             editor.create_model(BookWithSlug)
    
  2812.         constraint = UniqueConstraint(
    
  2813.             Upper("title"),
    
  2814.             Lower("slug"),
    
  2815.             name="func_upper_lower_unq",
    
  2816.         )
    
  2817.         # Add constraint.
    
  2818.         with connection.schema_editor() as editor:
    
  2819.             editor.add_constraint(BookWithSlug, constraint)
    
  2820.             sql = constraint.create_sql(BookWithSlug, editor)
    
  2821.         table = BookWithSlug._meta.db_table
    
  2822.         constraints = self.get_constraints(table)
    
  2823.         self.assertIn(constraint.name, constraints)
    
  2824.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2825.         # SQL contains database functions.
    
  2826.         self.assertIs(sql.references_column(table, "title"), True)
    
  2827.         self.assertIs(sql.references_column(table, "slug"), True)
    
  2828.         sql = str(sql)
    
  2829.         self.assertIn("UPPER(%s)" % editor.quote_name("title"), sql)
    
  2830.         self.assertIn("LOWER(%s)" % editor.quote_name("slug"), sql)
    
  2831.         self.assertLess(sql.index("UPPER"), sql.index("LOWER"))
    
  2832.         # Remove constraint.
    
  2833.         with connection.schema_editor() as editor:
    
  2834.             editor.remove_constraint(BookWithSlug, constraint)
    
  2835.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2836. 
    
  2837.     @skipUnlessDBFeature("supports_expression_indexes")
    
  2838.     def test_unique_constraint_field_and_expression(self):
    
  2839.         with connection.schema_editor() as editor:
    
  2840.             editor.create_model(Author)
    
  2841.         constraint = UniqueConstraint(
    
  2842.             F("height").desc(),
    
  2843.             "uuid",
    
  2844.             Lower("name").asc(),
    
  2845.             name="func_f_lower_field_unq",
    
  2846.         )
    
  2847.         # Add constraint.
    
  2848.         with connection.schema_editor() as editor:
    
  2849.             editor.add_constraint(Author, constraint)
    
  2850.             sql = constraint.create_sql(Author, editor)
    
  2851.         table = Author._meta.db_table
    
  2852.         if connection.features.supports_index_column_ordering:
    
  2853.             self.assertIndexOrder(table, constraint.name, ["DESC", "ASC", "ASC"])
    
  2854.         constraints = self.get_constraints(table)
    
  2855.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2856.         self.assertEqual(len(constraints[constraint.name]["columns"]), 3)
    
  2857.         self.assertEqual(constraints[constraint.name]["columns"][1], "uuid")
    
  2858.         # SQL contains database functions and columns.
    
  2859.         self.assertIs(sql.references_column(table, "height"), True)
    
  2860.         self.assertIs(sql.references_column(table, "name"), True)
    
  2861.         self.assertIs(sql.references_column(table, "uuid"), True)
    
  2862.         self.assertIn("LOWER(%s)" % editor.quote_name("name"), str(sql))
    
  2863.         # Remove constraint.
    
  2864.         with connection.schema_editor() as editor:
    
  2865.             editor.remove_constraint(Author, constraint)
    
  2866.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2867. 
    
  2868.     @skipUnlessDBFeature("supports_expression_indexes", "supports_partial_indexes")
    
  2869.     def test_func_unique_constraint_partial(self):
    
  2870.         with connection.schema_editor() as editor:
    
  2871.             editor.create_model(Author)
    
  2872.         constraint = UniqueConstraint(
    
  2873.             Upper("name"),
    
  2874.             name="func_upper_cond_weight_uq",
    
  2875.             condition=Q(weight__isnull=False),
    
  2876.         )
    
  2877.         # Add constraint.
    
  2878.         with connection.schema_editor() as editor:
    
  2879.             editor.add_constraint(Author, constraint)
    
  2880.             sql = constraint.create_sql(Author, editor)
    
  2881.         table = Author._meta.db_table
    
  2882.         constraints = self.get_constraints(table)
    
  2883.         self.assertIn(constraint.name, constraints)
    
  2884.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2885.         self.assertIs(sql.references_column(table, "name"), True)
    
  2886.         self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql))
    
  2887.         self.assertIn(
    
  2888.             "WHERE %s IS NOT NULL" % editor.quote_name("weight"),
    
  2889.             str(sql),
    
  2890.         )
    
  2891.         # Remove constraint.
    
  2892.         with connection.schema_editor() as editor:
    
  2893.             editor.remove_constraint(Author, constraint)
    
  2894.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2895. 
    
  2896.     @skipUnlessDBFeature("supports_expression_indexes", "supports_covering_indexes")
    
  2897.     def test_func_unique_constraint_covering(self):
    
  2898.         with connection.schema_editor() as editor:
    
  2899.             editor.create_model(Author)
    
  2900.         constraint = UniqueConstraint(
    
  2901.             Upper("name"),
    
  2902.             name="func_upper_covering_uq",
    
  2903.             include=["weight", "height"],
    
  2904.         )
    
  2905.         # Add constraint.
    
  2906.         with connection.schema_editor() as editor:
    
  2907.             editor.add_constraint(Author, constraint)
    
  2908.             sql = constraint.create_sql(Author, editor)
    
  2909.         table = Author._meta.db_table
    
  2910.         constraints = self.get_constraints(table)
    
  2911.         self.assertIn(constraint.name, constraints)
    
  2912.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2913.         self.assertEqual(
    
  2914.             constraints[constraint.name]["columns"],
    
  2915.             [None, "weight", "height"],
    
  2916.         )
    
  2917.         self.assertIs(sql.references_column(table, "name"), True)
    
  2918.         self.assertIs(sql.references_column(table, "weight"), True)
    
  2919.         self.assertIs(sql.references_column(table, "height"), True)
    
  2920.         self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql))
    
  2921.         self.assertIn(
    
  2922.             "INCLUDE (%s, %s)"
    
  2923.             % (
    
  2924.                 editor.quote_name("weight"),
    
  2925.                 editor.quote_name("height"),
    
  2926.             ),
    
  2927.             str(sql),
    
  2928.         )
    
  2929.         # Remove constraint.
    
  2930.         with connection.schema_editor() as editor:
    
  2931.             editor.remove_constraint(Author, constraint)
    
  2932.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2933. 
    
  2934.     @skipUnlessDBFeature("supports_expression_indexes")
    
  2935.     def test_func_unique_constraint_lookups(self):
    
  2936.         with connection.schema_editor() as editor:
    
  2937.             editor.create_model(Author)
    
  2938.         with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs):
    
  2939.             constraint = UniqueConstraint(
    
  2940.                 F("name__lower"),
    
  2941.                 F("weight__abs"),
    
  2942.                 name="func_lower_abs_lookup_uq",
    
  2943.             )
    
  2944.             # Add constraint.
    
  2945.             with connection.schema_editor() as editor:
    
  2946.                 editor.add_constraint(Author, constraint)
    
  2947.                 sql = constraint.create_sql(Author, editor)
    
  2948.             table = Author._meta.db_table
    
  2949.             constraints = self.get_constraints(table)
    
  2950.             self.assertIn(constraint.name, constraints)
    
  2951.             self.assertIs(constraints[constraint.name]["unique"], True)
    
  2952.             # SQL contains columns.
    
  2953.             self.assertIs(sql.references_column(table, "name"), True)
    
  2954.             self.assertIs(sql.references_column(table, "weight"), True)
    
  2955.             # Remove constraint.
    
  2956.             with connection.schema_editor() as editor:
    
  2957.                 editor.remove_constraint(Author, constraint)
    
  2958.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2959. 
    
  2960.     @skipUnlessDBFeature("supports_expression_indexes")
    
  2961.     def test_func_unique_constraint_collate(self):
    
  2962.         collation = connection.features.test_collations.get("non_default")
    
  2963.         if not collation:
    
  2964.             self.skipTest("This backend does not support case-insensitive collations.")
    
  2965.         with connection.schema_editor() as editor:
    
  2966.             editor.create_model(Author)
    
  2967.             editor.create_model(BookWithSlug)
    
  2968.         constraint = UniqueConstraint(
    
  2969.             Collate(F("title"), collation=collation).desc(),
    
  2970.             Collate("slug", collation=collation),
    
  2971.             name="func_collate_uq",
    
  2972.         )
    
  2973.         # Add constraint.
    
  2974.         with connection.schema_editor() as editor:
    
  2975.             editor.add_constraint(BookWithSlug, constraint)
    
  2976.             sql = constraint.create_sql(BookWithSlug, editor)
    
  2977.         table = BookWithSlug._meta.db_table
    
  2978.         constraints = self.get_constraints(table)
    
  2979.         self.assertIn(constraint.name, constraints)
    
  2980.         self.assertIs(constraints[constraint.name]["unique"], True)
    
  2981.         if connection.features.supports_index_column_ordering:
    
  2982.             self.assertIndexOrder(table, constraint.name, ["DESC", "ASC"])
    
  2983.         # SQL contains columns and a collation.
    
  2984.         self.assertIs(sql.references_column(table, "title"), True)
    
  2985.         self.assertIs(sql.references_column(table, "slug"), True)
    
  2986.         self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql))
    
  2987.         # Remove constraint.
    
  2988.         with connection.schema_editor() as editor:
    
  2989.             editor.remove_constraint(BookWithSlug, constraint)
    
  2990.         self.assertNotIn(constraint.name, self.get_constraints(table))
    
  2991. 
    
  2992.     @skipIfDBFeature("supports_expression_indexes")
    
  2993.     def test_func_unique_constraint_unsupported(self):
    
  2994.         # UniqueConstraint is ignored on databases that don't support indexes on
    
  2995.         # expressions.
    
  2996.         with connection.schema_editor() as editor:
    
  2997.             editor.create_model(Author)
    
  2998.         constraint = UniqueConstraint(F("name"), name="func_name_uq")
    
  2999.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  3000.             self.assertIsNone(editor.add_constraint(Author, constraint))
    
  3001.             self.assertIsNone(editor.remove_constraint(Author, constraint))
    
  3002. 
    
  3003.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3004.     def test_func_unique_constraint_nonexistent_field(self):
    
  3005.         constraint = UniqueConstraint(Lower("nonexistent"), name="func_nonexistent_uq")
    
  3006.         msg = (
    
  3007.             "Cannot resolve keyword 'nonexistent' into field. Choices are: "
    
  3008.             "height, id, name, uuid, weight"
    
  3009.         )
    
  3010.         with self.assertRaisesMessage(FieldError, msg):
    
  3011.             with connection.schema_editor() as editor:
    
  3012.                 editor.add_constraint(Author, constraint)
    
  3013. 
    
  3014.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3015.     def test_func_unique_constraint_nondeterministic(self):
    
  3016.         with connection.schema_editor() as editor:
    
  3017.             editor.create_model(Author)
    
  3018.         constraint = UniqueConstraint(Random(), name="func_random_uq")
    
  3019.         with connection.schema_editor() as editor:
    
  3020.             with self.assertRaises(DatabaseError):
    
  3021.                 editor.add_constraint(Author, constraint)
    
  3022. 
    
  3023.     def test_index_together(self):
    
  3024.         """
    
  3025.         Tests removing and adding index_together constraints on a model.
    
  3026.         """
    
  3027.         # Create the table
    
  3028.         with connection.schema_editor() as editor:
    
  3029.             editor.create_model(Tag)
    
  3030.         # Ensure there's no index on the year/slug columns first
    
  3031.         self.assertIs(
    
  3032.             any(
    
  3033.                 c["index"]
    
  3034.                 for c in self.get_constraints("schema_tag").values()
    
  3035.                 if c["columns"] == ["slug", "title"]
    
  3036.             ),
    
  3037.             False,
    
  3038.         )
    
  3039.         # Alter the model to add an index
    
  3040.         with connection.schema_editor() as editor:
    
  3041.             editor.alter_index_together(Tag, [], [("slug", "title")])
    
  3042.         # Ensure there is now an index
    
  3043.         self.assertIs(
    
  3044.             any(
    
  3045.                 c["index"]
    
  3046.                 for c in self.get_constraints("schema_tag").values()
    
  3047.                 if c["columns"] == ["slug", "title"]
    
  3048.             ),
    
  3049.             True,
    
  3050.         )
    
  3051.         # Alter it back
    
  3052.         new_field2 = SlugField(unique=True)
    
  3053.         new_field2.set_attributes_from_name("slug")
    
  3054.         with connection.schema_editor() as editor:
    
  3055.             editor.alter_index_together(Tag, [("slug", "title")], [])
    
  3056.         # Ensure there's no index
    
  3057.         self.assertIs(
    
  3058.             any(
    
  3059.                 c["index"]
    
  3060.                 for c in self.get_constraints("schema_tag").values()
    
  3061.                 if c["columns"] == ["slug", "title"]
    
  3062.             ),
    
  3063.             False,
    
  3064.         )
    
  3065. 
    
  3066.     def test_index_together_with_fk(self):
    
  3067.         """
    
  3068.         Tests removing and adding index_together constraints that include
    
  3069.         a foreign key.
    
  3070.         """
    
  3071.         # Create the table
    
  3072.         with connection.schema_editor() as editor:
    
  3073.             editor.create_model(Author)
    
  3074.             editor.create_model(Book)
    
  3075.         # Ensure the fields are unique to begin with
    
  3076.         self.assertEqual(Book._meta.index_together, ())
    
  3077.         # Add the unique_together constraint
    
  3078.         with connection.schema_editor() as editor:
    
  3079.             editor.alter_index_together(Book, [], [["author", "title"]])
    
  3080.         # Alter it back
    
  3081.         with connection.schema_editor() as editor:
    
  3082.             editor.alter_index_together(Book, [["author", "title"]], [])
    
  3083. 
    
  3084.     def test_create_index_together(self):
    
  3085.         """
    
  3086.         Tests creating models with index_together already defined
    
  3087.         """
    
  3088.         # Create the table
    
  3089.         with connection.schema_editor() as editor:
    
  3090.             editor.create_model(TagIndexed)
    
  3091.         # Ensure there is an index
    
  3092.         self.assertIs(
    
  3093.             any(
    
  3094.                 c["index"]
    
  3095.                 for c in self.get_constraints("schema_tagindexed").values()
    
  3096.                 if c["columns"] == ["slug", "title"]
    
  3097.             ),
    
  3098.             True,
    
  3099.         )
    
  3100. 
    
  3101.     @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields")
    
  3102.     def test_remove_index_together_does_not_remove_meta_indexes(self):
    
  3103.         with connection.schema_editor() as editor:
    
  3104.             editor.create_model(AuthorWithIndexedNameAndBirthday)
    
  3105.         self.local_models = [AuthorWithIndexedNameAndBirthday]
    
  3106.         # Add the custom index
    
  3107.         index = Index(fields=["name", "birthday"], name="author_name_birthday_idx")
    
  3108.         custom_index_name = index.name
    
  3109.         AuthorWithIndexedNameAndBirthday._meta.indexes = [index]
    
  3110.         with connection.schema_editor() as editor:
    
  3111.             editor.add_index(AuthorWithIndexedNameAndBirthday, index)
    
  3112.         # Ensure the indexes exist
    
  3113.         constraints = self.get_constraints(
    
  3114.             AuthorWithIndexedNameAndBirthday._meta.db_table
    
  3115.         )
    
  3116.         self.assertIn(custom_index_name, constraints)
    
  3117.         other_constraints = [
    
  3118.             name
    
  3119.             for name, details in constraints.items()
    
  3120.             if details["columns"] == ["name", "birthday"]
    
  3121.             and details["index"]
    
  3122.             and name != custom_index_name
    
  3123.         ]
    
  3124.         self.assertEqual(len(other_constraints), 1)
    
  3125.         # Remove index together
    
  3126.         index_together = AuthorWithIndexedNameAndBirthday._meta.index_together
    
  3127.         with connection.schema_editor() as editor:
    
  3128.             editor.alter_index_together(
    
  3129.                 AuthorWithIndexedNameAndBirthday, index_together, []
    
  3130.             )
    
  3131.         constraints = self.get_constraints(
    
  3132.             AuthorWithIndexedNameAndBirthday._meta.db_table
    
  3133.         )
    
  3134.         self.assertIn(custom_index_name, constraints)
    
  3135.         other_constraints = [
    
  3136.             name
    
  3137.             for name, details in constraints.items()
    
  3138.             if details["columns"] == ["name", "birthday"]
    
  3139.             and details["index"]
    
  3140.             and name != custom_index_name
    
  3141.         ]
    
  3142.         self.assertEqual(len(other_constraints), 0)
    
  3143.         # Re-add index together
    
  3144.         with connection.schema_editor() as editor:
    
  3145.             editor.alter_index_together(
    
  3146.                 AuthorWithIndexedNameAndBirthday, [], index_together
    
  3147.             )
    
  3148.         constraints = self.get_constraints(
    
  3149.             AuthorWithIndexedNameAndBirthday._meta.db_table
    
  3150.         )
    
  3151.         self.assertIn(custom_index_name, constraints)
    
  3152.         other_constraints = [
    
  3153.             name
    
  3154.             for name, details in constraints.items()
    
  3155.             if details["columns"] == ["name", "birthday"]
    
  3156.             and details["index"]
    
  3157.             and name != custom_index_name
    
  3158.         ]
    
  3159.         self.assertEqual(len(other_constraints), 1)
    
  3160.         # Drop the index
    
  3161.         with connection.schema_editor() as editor:
    
  3162.             AuthorWithIndexedNameAndBirthday._meta.indexes = []
    
  3163.             editor.remove_index(AuthorWithIndexedNameAndBirthday, index)
    
  3164. 
    
  3165.     @isolate_apps("schema")
    
  3166.     def test_db_table(self):
    
  3167.         """
    
  3168.         Tests renaming of the table
    
  3169.         """
    
  3170. 
    
  3171.         class Author(Model):
    
  3172.             name = CharField(max_length=255)
    
  3173. 
    
  3174.             class Meta:
    
  3175.                 app_label = "schema"
    
  3176. 
    
  3177.         class Book(Model):
    
  3178.             author = ForeignKey(Author, CASCADE)
    
  3179. 
    
  3180.             class Meta:
    
  3181.                 app_label = "schema"
    
  3182. 
    
  3183.         # Create the table and one referring it.
    
  3184.         with connection.schema_editor() as editor:
    
  3185.             editor.create_model(Author)
    
  3186.             editor.create_model(Book)
    
  3187.         # Ensure the table is there to begin with
    
  3188.         columns = self.column_classes(Author)
    
  3189.         self.assertEqual(
    
  3190.             columns["name"][0],
    
  3191.             connection.features.introspected_field_types["CharField"],
    
  3192.         )
    
  3193.         # Alter the table
    
  3194.         with connection.schema_editor(
    
  3195.             atomic=connection.features.supports_atomic_references_rename
    
  3196.         ) as editor:
    
  3197.             editor.alter_db_table(Author, "schema_author", "schema_otherauthor")
    
  3198.         Author._meta.db_table = "schema_otherauthor"
    
  3199.         columns = self.column_classes(Author)
    
  3200.         self.assertEqual(
    
  3201.             columns["name"][0],
    
  3202.             connection.features.introspected_field_types["CharField"],
    
  3203.         )
    
  3204.         # Ensure the foreign key reference was updated
    
  3205.         self.assertForeignKeyExists(Book, "author_id", "schema_otherauthor")
    
  3206.         # Alter the table again
    
  3207.         with connection.schema_editor(
    
  3208.             atomic=connection.features.supports_atomic_references_rename
    
  3209.         ) as editor:
    
  3210.             editor.alter_db_table(Author, "schema_otherauthor", "schema_author")
    
  3211.         # Ensure the table is still there
    
  3212.         Author._meta.db_table = "schema_author"
    
  3213.         columns = self.column_classes(Author)
    
  3214.         self.assertEqual(
    
  3215.             columns["name"][0],
    
  3216.             connection.features.introspected_field_types["CharField"],
    
  3217.         )
    
  3218. 
    
  3219.     def test_add_remove_index(self):
    
  3220.         """
    
  3221.         Tests index addition and removal
    
  3222.         """
    
  3223.         # Create the table
    
  3224.         with connection.schema_editor() as editor:
    
  3225.             editor.create_model(Author)
    
  3226.         # Ensure the table is there and has no index
    
  3227.         self.assertNotIn("title", self.get_indexes(Author._meta.db_table))
    
  3228.         # Add the index
    
  3229.         index = Index(fields=["name"], name="author_title_idx")
    
  3230.         with connection.schema_editor() as editor:
    
  3231.             editor.add_index(Author, index)
    
  3232.         self.assertIn("name", self.get_indexes(Author._meta.db_table))
    
  3233.         # Drop the index
    
  3234.         with connection.schema_editor() as editor:
    
  3235.             editor.remove_index(Author, index)
    
  3236.         self.assertNotIn("name", self.get_indexes(Author._meta.db_table))
    
  3237. 
    
  3238.     def test_remove_db_index_doesnt_remove_custom_indexes(self):
    
  3239.         """
    
  3240.         Changing db_index to False doesn't remove indexes from Meta.indexes.
    
  3241.         """
    
  3242.         with connection.schema_editor() as editor:
    
  3243.             editor.create_model(AuthorWithIndexedName)
    
  3244.         self.local_models = [AuthorWithIndexedName]
    
  3245.         # Ensure the table has its index
    
  3246.         self.assertIn("name", self.get_indexes(AuthorWithIndexedName._meta.db_table))
    
  3247. 
    
  3248.         # Add the custom index
    
  3249.         index = Index(fields=["-name"], name="author_name_idx")
    
  3250.         author_index_name = index.name
    
  3251.         with connection.schema_editor() as editor:
    
  3252.             db_index_name = editor._create_index_name(
    
  3253.                 table_name=AuthorWithIndexedName._meta.db_table,
    
  3254.                 column_names=("name",),
    
  3255.             )
    
  3256.         try:
    
  3257.             AuthorWithIndexedName._meta.indexes = [index]
    
  3258.             with connection.schema_editor() as editor:
    
  3259.                 editor.add_index(AuthorWithIndexedName, index)
    
  3260.             old_constraints = self.get_constraints(AuthorWithIndexedName._meta.db_table)
    
  3261.             self.assertIn(author_index_name, old_constraints)
    
  3262.             self.assertIn(db_index_name, old_constraints)
    
  3263.             # Change name field to db_index=False
    
  3264.             old_field = AuthorWithIndexedName._meta.get_field("name")
    
  3265.             new_field = CharField(max_length=255)
    
  3266.             new_field.set_attributes_from_name("name")
    
  3267.             with connection.schema_editor() as editor:
    
  3268.                 editor.alter_field(
    
  3269.                     AuthorWithIndexedName, old_field, new_field, strict=True
    
  3270.                 )
    
  3271.             new_constraints = self.get_constraints(AuthorWithIndexedName._meta.db_table)
    
  3272.             self.assertNotIn(db_index_name, new_constraints)
    
  3273.             # The index from Meta.indexes is still in the database.
    
  3274.             self.assertIn(author_index_name, new_constraints)
    
  3275.             # Drop the index
    
  3276.             with connection.schema_editor() as editor:
    
  3277.                 editor.remove_index(AuthorWithIndexedName, index)
    
  3278.         finally:
    
  3279.             AuthorWithIndexedName._meta.indexes = []
    
  3280. 
    
  3281.     def test_order_index(self):
    
  3282.         """
    
  3283.         Indexes defined with ordering (ASC/DESC) defined on column
    
  3284.         """
    
  3285.         with connection.schema_editor() as editor:
    
  3286.             editor.create_model(Author)
    
  3287.         # The table doesn't have an index
    
  3288.         self.assertNotIn("title", self.get_indexes(Author._meta.db_table))
    
  3289.         index_name = "author_name_idx"
    
  3290.         # Add the index
    
  3291.         index = Index(fields=["name", "-weight"], name=index_name)
    
  3292.         with connection.schema_editor() as editor:
    
  3293.             editor.add_index(Author, index)
    
  3294.         if connection.features.supports_index_column_ordering:
    
  3295.             self.assertIndexOrder(Author._meta.db_table, index_name, ["ASC", "DESC"])
    
  3296.         # Drop the index
    
  3297.         with connection.schema_editor() as editor:
    
  3298.             editor.remove_index(Author, index)
    
  3299. 
    
  3300.     def test_indexes(self):
    
  3301.         """
    
  3302.         Tests creation/altering of indexes
    
  3303.         """
    
  3304.         # Create the table
    
  3305.         with connection.schema_editor() as editor:
    
  3306.             editor.create_model(Author)
    
  3307.             editor.create_model(Book)
    
  3308.         # Ensure the table is there and has the right index
    
  3309.         self.assertIn(
    
  3310.             "title",
    
  3311.             self.get_indexes(Book._meta.db_table),
    
  3312.         )
    
  3313.         # Alter to remove the index
    
  3314.         old_field = Book._meta.get_field("title")
    
  3315.         new_field = CharField(max_length=100, db_index=False)
    
  3316.         new_field.set_attributes_from_name("title")
    
  3317.         with connection.schema_editor() as editor:
    
  3318.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  3319.         # Ensure the table is there and has no index
    
  3320.         self.assertNotIn(
    
  3321.             "title",
    
  3322.             self.get_indexes(Book._meta.db_table),
    
  3323.         )
    
  3324.         # Alter to re-add the index
    
  3325.         new_field2 = Book._meta.get_field("title")
    
  3326.         with connection.schema_editor() as editor:
    
  3327.             editor.alter_field(Book, new_field, new_field2, strict=True)
    
  3328.         # Ensure the table is there and has the index again
    
  3329.         self.assertIn(
    
  3330.             "title",
    
  3331.             self.get_indexes(Book._meta.db_table),
    
  3332.         )
    
  3333.         # Add a unique column, verify that creates an implicit index
    
  3334.         new_field3 = BookWithSlug._meta.get_field("slug")
    
  3335.         with connection.schema_editor() as editor:
    
  3336.             editor.add_field(Book, new_field3)
    
  3337.         self.assertIn(
    
  3338.             "slug",
    
  3339.             self.get_uniques(Book._meta.db_table),
    
  3340.         )
    
  3341.         # Remove the unique, check the index goes with it
    
  3342.         new_field4 = CharField(max_length=20, unique=False)
    
  3343.         new_field4.set_attributes_from_name("slug")
    
  3344.         with connection.schema_editor() as editor:
    
  3345.             editor.alter_field(BookWithSlug, new_field3, new_field4, strict=True)
    
  3346.         self.assertNotIn(
    
  3347.             "slug",
    
  3348.             self.get_uniques(Book._meta.db_table),
    
  3349.         )
    
  3350. 
    
  3351.     def test_text_field_with_db_index(self):
    
  3352.         with connection.schema_editor() as editor:
    
  3353.             editor.create_model(AuthorTextFieldWithIndex)
    
  3354.         # The text_field index is present if the database supports it.
    
  3355.         assertion = (
    
  3356.             self.assertIn
    
  3357.             if connection.features.supports_index_on_text_field
    
  3358.             else self.assertNotIn
    
  3359.         )
    
  3360.         assertion(
    
  3361.             "text_field", self.get_indexes(AuthorTextFieldWithIndex._meta.db_table)
    
  3362.         )
    
  3363. 
    
  3364.     def _index_expressions_wrappers(self):
    
  3365.         index_expression = IndexExpression()
    
  3366.         index_expression.set_wrapper_classes(connection)
    
  3367.         return ", ".join(
    
  3368.             [
    
  3369.                 wrapper_cls.__qualname__
    
  3370.                 for wrapper_cls in index_expression.wrapper_classes
    
  3371.             ]
    
  3372.         )
    
  3373. 
    
  3374.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3375.     def test_func_index_multiple_wrapper_references(self):
    
  3376.         index = Index(OrderBy(F("name").desc(), descending=True), name="name")
    
  3377.         msg = (
    
  3378.             "Multiple references to %s can't be used in an indexed expression."
    
  3379.             % self._index_expressions_wrappers()
    
  3380.         )
    
  3381.         with connection.schema_editor() as editor:
    
  3382.             with self.assertRaisesMessage(ValueError, msg):
    
  3383.                 editor.add_index(Author, index)
    
  3384. 
    
  3385.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3386.     def test_func_index_invalid_topmost_expressions(self):
    
  3387.         index = Index(Upper(F("name").desc()), name="name")
    
  3388.         msg = (
    
  3389.             "%s must be topmost expressions in an indexed expression."
    
  3390.             % self._index_expressions_wrappers()
    
  3391.         )
    
  3392.         with connection.schema_editor() as editor:
    
  3393.             with self.assertRaisesMessage(ValueError, msg):
    
  3394.                 editor.add_index(Author, index)
    
  3395. 
    
  3396.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3397.     def test_func_index(self):
    
  3398.         with connection.schema_editor() as editor:
    
  3399.             editor.create_model(Author)
    
  3400.         index = Index(Lower("name").desc(), name="func_lower_idx")
    
  3401.         # Add index.
    
  3402.         with connection.schema_editor() as editor:
    
  3403.             editor.add_index(Author, index)
    
  3404.             sql = index.create_sql(Author, editor)
    
  3405.         table = Author._meta.db_table
    
  3406.         if connection.features.supports_index_column_ordering:
    
  3407.             self.assertIndexOrder(table, index.name, ["DESC"])
    
  3408.         # SQL contains a database function.
    
  3409.         self.assertIs(sql.references_column(table, "name"), True)
    
  3410.         self.assertIn("LOWER(%s)" % editor.quote_name("name"), str(sql))
    
  3411.         # Remove index.
    
  3412.         with connection.schema_editor() as editor:
    
  3413.             editor.remove_index(Author, index)
    
  3414.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3415. 
    
  3416.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3417.     def test_func_index_f(self):
    
  3418.         with connection.schema_editor() as editor:
    
  3419.             editor.create_model(Tag)
    
  3420.         index = Index("slug", F("title").desc(), name="func_f_idx")
    
  3421.         # Add index.
    
  3422.         with connection.schema_editor() as editor:
    
  3423.             editor.add_index(Tag, index)
    
  3424.             sql = index.create_sql(Tag, editor)
    
  3425.         table = Tag._meta.db_table
    
  3426.         self.assertIn(index.name, self.get_constraints(table))
    
  3427.         if connection.features.supports_index_column_ordering:
    
  3428.             self.assertIndexOrder(Tag._meta.db_table, index.name, ["ASC", "DESC"])
    
  3429.         # SQL contains columns.
    
  3430.         self.assertIs(sql.references_column(table, "slug"), True)
    
  3431.         self.assertIs(sql.references_column(table, "title"), True)
    
  3432.         # Remove index.
    
  3433.         with connection.schema_editor() as editor:
    
  3434.             editor.remove_index(Tag, index)
    
  3435.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3436. 
    
  3437.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3438.     def test_func_index_lookups(self):
    
  3439.         with connection.schema_editor() as editor:
    
  3440.             editor.create_model(Author)
    
  3441.         with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs):
    
  3442.             index = Index(
    
  3443.                 F("name__lower"),
    
  3444.                 F("weight__abs"),
    
  3445.                 name="func_lower_abs_lookup_idx",
    
  3446.             )
    
  3447.             # Add index.
    
  3448.             with connection.schema_editor() as editor:
    
  3449.                 editor.add_index(Author, index)
    
  3450.                 sql = index.create_sql(Author, editor)
    
  3451.         table = Author._meta.db_table
    
  3452.         self.assertIn(index.name, self.get_constraints(table))
    
  3453.         # SQL contains columns.
    
  3454.         self.assertIs(sql.references_column(table, "name"), True)
    
  3455.         self.assertIs(sql.references_column(table, "weight"), True)
    
  3456.         # Remove index.
    
  3457.         with connection.schema_editor() as editor:
    
  3458.             editor.remove_index(Author, index)
    
  3459.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3460. 
    
  3461.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3462.     def test_composite_func_index(self):
    
  3463.         with connection.schema_editor() as editor:
    
  3464.             editor.create_model(Author)
    
  3465.         index = Index(Lower("name"), Upper("name"), name="func_lower_upper_idx")
    
  3466.         # Add index.
    
  3467.         with connection.schema_editor() as editor:
    
  3468.             editor.add_index(Author, index)
    
  3469.             sql = index.create_sql(Author, editor)
    
  3470.         table = Author._meta.db_table
    
  3471.         self.assertIn(index.name, self.get_constraints(table))
    
  3472.         # SQL contains database functions.
    
  3473.         self.assertIs(sql.references_column(table, "name"), True)
    
  3474.         sql = str(sql)
    
  3475.         self.assertIn("LOWER(%s)" % editor.quote_name("name"), sql)
    
  3476.         self.assertIn("UPPER(%s)" % editor.quote_name("name"), sql)
    
  3477.         self.assertLess(sql.index("LOWER"), sql.index("UPPER"))
    
  3478.         # Remove index.
    
  3479.         with connection.schema_editor() as editor:
    
  3480.             editor.remove_index(Author, index)
    
  3481.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3482. 
    
  3483.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3484.     def test_composite_func_index_field_and_expression(self):
    
  3485.         with connection.schema_editor() as editor:
    
  3486.             editor.create_model(Author)
    
  3487.             editor.create_model(Book)
    
  3488.         index = Index(
    
  3489.             F("author").desc(),
    
  3490.             Lower("title").asc(),
    
  3491.             "pub_date",
    
  3492.             name="func_f_lower_field_idx",
    
  3493.         )
    
  3494.         # Add index.
    
  3495.         with connection.schema_editor() as editor:
    
  3496.             editor.add_index(Book, index)
    
  3497.             sql = index.create_sql(Book, editor)
    
  3498.         table = Book._meta.db_table
    
  3499.         constraints = self.get_constraints(table)
    
  3500.         if connection.features.supports_index_column_ordering:
    
  3501.             self.assertIndexOrder(table, index.name, ["DESC", "ASC", "ASC"])
    
  3502.         self.assertEqual(len(constraints[index.name]["columns"]), 3)
    
  3503.         self.assertEqual(constraints[index.name]["columns"][2], "pub_date")
    
  3504.         # SQL contains database functions and columns.
    
  3505.         self.assertIs(sql.references_column(table, "author_id"), True)
    
  3506.         self.assertIs(sql.references_column(table, "title"), True)
    
  3507.         self.assertIs(sql.references_column(table, "pub_date"), True)
    
  3508.         self.assertIn("LOWER(%s)" % editor.quote_name("title"), str(sql))
    
  3509.         # Remove index.
    
  3510.         with connection.schema_editor() as editor:
    
  3511.             editor.remove_index(Book, index)
    
  3512.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3513. 
    
  3514.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3515.     @isolate_apps("schema")
    
  3516.     def test_func_index_f_decimalfield(self):
    
  3517.         class Node(Model):
    
  3518.             value = DecimalField(max_digits=5, decimal_places=2)
    
  3519. 
    
  3520.             class Meta:
    
  3521.                 app_label = "schema"
    
  3522. 
    
  3523.         with connection.schema_editor() as editor:
    
  3524.             editor.create_model(Node)
    
  3525.         index = Index(F("value"), name="func_f_decimalfield_idx")
    
  3526.         # Add index.
    
  3527.         with connection.schema_editor() as editor:
    
  3528.             editor.add_index(Node, index)
    
  3529.             sql = index.create_sql(Node, editor)
    
  3530.         table = Node._meta.db_table
    
  3531.         self.assertIn(index.name, self.get_constraints(table))
    
  3532.         self.assertIs(sql.references_column(table, "value"), True)
    
  3533.         # SQL doesn't contain casting.
    
  3534.         self.assertNotIn("CAST", str(sql))
    
  3535.         # Remove index.
    
  3536.         with connection.schema_editor() as editor:
    
  3537.             editor.remove_index(Node, index)
    
  3538.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3539. 
    
  3540.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3541.     def test_func_index_cast(self):
    
  3542.         with connection.schema_editor() as editor:
    
  3543.             editor.create_model(Author)
    
  3544.         index = Index(Cast("weight", FloatField()), name="func_cast_idx")
    
  3545.         # Add index.
    
  3546.         with connection.schema_editor() as editor:
    
  3547.             editor.add_index(Author, index)
    
  3548.             sql = index.create_sql(Author, editor)
    
  3549.         table = Author._meta.db_table
    
  3550.         self.assertIn(index.name, self.get_constraints(table))
    
  3551.         self.assertIs(sql.references_column(table, "weight"), True)
    
  3552.         # Remove index.
    
  3553.         with connection.schema_editor() as editor:
    
  3554.             editor.remove_index(Author, index)
    
  3555.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3556. 
    
  3557.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3558.     def test_func_index_collate(self):
    
  3559.         collation = connection.features.test_collations.get("non_default")
    
  3560.         if not collation:
    
  3561.             self.skipTest("This backend does not support case-insensitive collations.")
    
  3562.         with connection.schema_editor() as editor:
    
  3563.             editor.create_model(Author)
    
  3564.             editor.create_model(BookWithSlug)
    
  3565.         index = Index(
    
  3566.             Collate(F("title"), collation=collation).desc(),
    
  3567.             Collate("slug", collation=collation),
    
  3568.             name="func_collate_idx",
    
  3569.         )
    
  3570.         # Add index.
    
  3571.         with connection.schema_editor() as editor:
    
  3572.             editor.add_index(BookWithSlug, index)
    
  3573.             sql = index.create_sql(BookWithSlug, editor)
    
  3574.         table = Book._meta.db_table
    
  3575.         self.assertIn(index.name, self.get_constraints(table))
    
  3576.         if connection.features.supports_index_column_ordering:
    
  3577.             self.assertIndexOrder(table, index.name, ["DESC", "ASC"])
    
  3578.         # SQL contains columns and a collation.
    
  3579.         self.assertIs(sql.references_column(table, "title"), True)
    
  3580.         self.assertIs(sql.references_column(table, "slug"), True)
    
  3581.         self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql))
    
  3582.         # Remove index.
    
  3583.         with connection.schema_editor() as editor:
    
  3584.             editor.remove_index(Book, index)
    
  3585.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3586. 
    
  3587.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3588.     @skipIfDBFeature("collate_as_index_expression")
    
  3589.     def test_func_index_collate_f_ordered(self):
    
  3590.         collation = connection.features.test_collations.get("non_default")
    
  3591.         if not collation:
    
  3592.             self.skipTest("This backend does not support case-insensitive collations.")
    
  3593.         with connection.schema_editor() as editor:
    
  3594.             editor.create_model(Author)
    
  3595.         index = Index(
    
  3596.             Collate(F("name").desc(), collation=collation),
    
  3597.             name="func_collate_f_desc_idx",
    
  3598.         )
    
  3599.         # Add index.
    
  3600.         with connection.schema_editor() as editor:
    
  3601.             editor.add_index(Author, index)
    
  3602.             sql = index.create_sql(Author, editor)
    
  3603.         table = Author._meta.db_table
    
  3604.         self.assertIn(index.name, self.get_constraints(table))
    
  3605.         if connection.features.supports_index_column_ordering:
    
  3606.             self.assertIndexOrder(table, index.name, ["DESC"])
    
  3607.         # SQL contains columns and a collation.
    
  3608.         self.assertIs(sql.references_column(table, "name"), True)
    
  3609.         self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql))
    
  3610.         # Remove index.
    
  3611.         with connection.schema_editor() as editor:
    
  3612.             editor.remove_index(Author, index)
    
  3613.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3614. 
    
  3615.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3616.     def test_func_index_calc(self):
    
  3617.         with connection.schema_editor() as editor:
    
  3618.             editor.create_model(Author)
    
  3619.         index = Index(F("height") / (F("weight") + Value(5)), name="func_calc_idx")
    
  3620.         # Add index.
    
  3621.         with connection.schema_editor() as editor:
    
  3622.             editor.add_index(Author, index)
    
  3623.             sql = index.create_sql(Author, editor)
    
  3624.         table = Author._meta.db_table
    
  3625.         self.assertIn(index.name, self.get_constraints(table))
    
  3626.         # SQL contains columns and expressions.
    
  3627.         self.assertIs(sql.references_column(table, "height"), True)
    
  3628.         self.assertIs(sql.references_column(table, "weight"), True)
    
  3629.         sql = str(sql)
    
  3630.         self.assertIs(
    
  3631.             sql.index(editor.quote_name("height"))
    
  3632.             < sql.index("/")
    
  3633.             < sql.index(editor.quote_name("weight"))
    
  3634.             < sql.index("+")
    
  3635.             < sql.index("5"),
    
  3636.             True,
    
  3637.         )
    
  3638.         # Remove index.
    
  3639.         with connection.schema_editor() as editor:
    
  3640.             editor.remove_index(Author, index)
    
  3641.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3642. 
    
  3643.     @skipUnlessDBFeature("supports_expression_indexes", "supports_json_field")
    
  3644.     @isolate_apps("schema")
    
  3645.     def test_func_index_json_key_transform(self):
    
  3646.         class JSONModel(Model):
    
  3647.             field = JSONField()
    
  3648. 
    
  3649.             class Meta:
    
  3650.                 app_label = "schema"
    
  3651. 
    
  3652.         with connection.schema_editor() as editor:
    
  3653.             editor.create_model(JSONModel)
    
  3654.         self.isolated_local_models = [JSONModel]
    
  3655.         index = Index("field__some_key", name="func_json_key_idx")
    
  3656.         with connection.schema_editor() as editor:
    
  3657.             editor.add_index(JSONModel, index)
    
  3658.             sql = index.create_sql(JSONModel, editor)
    
  3659.         table = JSONModel._meta.db_table
    
  3660.         self.assertIn(index.name, self.get_constraints(table))
    
  3661.         self.assertIs(sql.references_column(table, "field"), True)
    
  3662.         with connection.schema_editor() as editor:
    
  3663.             editor.remove_index(JSONModel, index)
    
  3664.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3665. 
    
  3666.     @skipUnlessDBFeature("supports_expression_indexes", "supports_json_field")
    
  3667.     @isolate_apps("schema")
    
  3668.     def test_func_index_json_key_transform_cast(self):
    
  3669.         class JSONModel(Model):
    
  3670.             field = JSONField()
    
  3671. 
    
  3672.             class Meta:
    
  3673.                 app_label = "schema"
    
  3674. 
    
  3675.         with connection.schema_editor() as editor:
    
  3676.             editor.create_model(JSONModel)
    
  3677.         self.isolated_local_models = [JSONModel]
    
  3678.         index = Index(
    
  3679.             Cast(KeyTextTransform("some_key", "field"), IntegerField()),
    
  3680.             name="func_json_key_cast_idx",
    
  3681.         )
    
  3682.         with connection.schema_editor() as editor:
    
  3683.             editor.add_index(JSONModel, index)
    
  3684.             sql = index.create_sql(JSONModel, editor)
    
  3685.         table = JSONModel._meta.db_table
    
  3686.         self.assertIn(index.name, self.get_constraints(table))
    
  3687.         self.assertIs(sql.references_column(table, "field"), True)
    
  3688.         with connection.schema_editor() as editor:
    
  3689.             editor.remove_index(JSONModel, index)
    
  3690.         self.assertNotIn(index.name, self.get_constraints(table))
    
  3691. 
    
  3692.     @skipIfDBFeature("supports_expression_indexes")
    
  3693.     def test_func_index_unsupported(self):
    
  3694.         # Index is ignored on databases that don't support indexes on
    
  3695.         # expressions.
    
  3696.         with connection.schema_editor() as editor:
    
  3697.             editor.create_model(Author)
    
  3698.         index = Index(F("name"), name="random_idx")
    
  3699.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  3700.             self.assertIsNone(editor.add_index(Author, index))
    
  3701.             self.assertIsNone(editor.remove_index(Author, index))
    
  3702. 
    
  3703.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3704.     def test_func_index_nonexistent_field(self):
    
  3705.         index = Index(Lower("nonexistent"), name="func_nonexistent_idx")
    
  3706.         msg = (
    
  3707.             "Cannot resolve keyword 'nonexistent' into field. Choices are: "
    
  3708.             "height, id, name, uuid, weight"
    
  3709.         )
    
  3710.         with self.assertRaisesMessage(FieldError, msg):
    
  3711.             with connection.schema_editor() as editor:
    
  3712.                 editor.add_index(Author, index)
    
  3713. 
    
  3714.     @skipUnlessDBFeature("supports_expression_indexes")
    
  3715.     def test_func_index_nondeterministic(self):
    
  3716.         with connection.schema_editor() as editor:
    
  3717.             editor.create_model(Author)
    
  3718.         index = Index(Random(), name="func_random_idx")
    
  3719.         with connection.schema_editor() as editor:
    
  3720.             with self.assertRaises(DatabaseError):
    
  3721.                 editor.add_index(Author, index)
    
  3722. 
    
  3723.     def test_primary_key(self):
    
  3724.         """
    
  3725.         Tests altering of the primary key
    
  3726.         """
    
  3727.         # Create the table
    
  3728.         with connection.schema_editor() as editor:
    
  3729.             editor.create_model(Tag)
    
  3730.         # Ensure the table is there and has the right PK
    
  3731.         self.assertEqual(self.get_primary_key(Tag._meta.db_table), "id")
    
  3732.         # Alter to change the PK
    
  3733.         id_field = Tag._meta.get_field("id")
    
  3734.         old_field = Tag._meta.get_field("slug")
    
  3735.         new_field = SlugField(primary_key=True)
    
  3736.         new_field.set_attributes_from_name("slug")
    
  3737.         new_field.model = Tag
    
  3738.         with connection.schema_editor() as editor:
    
  3739.             editor.remove_field(Tag, id_field)
    
  3740.             editor.alter_field(Tag, old_field, new_field)
    
  3741.         # Ensure the PK changed
    
  3742.         self.assertNotIn(
    
  3743.             "id",
    
  3744.             self.get_indexes(Tag._meta.db_table),
    
  3745.         )
    
  3746.         self.assertEqual(self.get_primary_key(Tag._meta.db_table), "slug")
    
  3747. 
    
  3748.     def test_alter_primary_key_the_same_name(self):
    
  3749.         with connection.schema_editor() as editor:
    
  3750.             editor.create_model(Thing)
    
  3751. 
    
  3752.         old_field = Thing._meta.get_field("when")
    
  3753.         new_field = CharField(max_length=2, primary_key=True)
    
  3754.         new_field.set_attributes_from_name("when")
    
  3755.         new_field.model = Thing
    
  3756.         with connection.schema_editor() as editor:
    
  3757.             editor.alter_field(Thing, old_field, new_field, strict=True)
    
  3758.         self.assertEqual(self.get_primary_key(Thing._meta.db_table), "when")
    
  3759.         with connection.schema_editor() as editor:
    
  3760.             editor.alter_field(Thing, new_field, old_field, strict=True)
    
  3761.         self.assertEqual(self.get_primary_key(Thing._meta.db_table), "when")
    
  3762. 
    
  3763.     def test_context_manager_exit(self):
    
  3764.         """
    
  3765.         Ensures transaction is correctly closed when an error occurs
    
  3766.         inside a SchemaEditor context.
    
  3767.         """
    
  3768. 
    
  3769.         class SomeError(Exception):
    
  3770.             pass
    
  3771. 
    
  3772.         try:
    
  3773.             with connection.schema_editor():
    
  3774.                 raise SomeError
    
  3775.         except SomeError:
    
  3776.             self.assertFalse(connection.in_atomic_block)
    
  3777. 
    
  3778.     @skipIfDBFeature("can_rollback_ddl")
    
  3779.     def test_unsupported_transactional_ddl_disallowed(self):
    
  3780.         message = (
    
  3781.             "Executing DDL statements while in a transaction on databases "
    
  3782.             "that can't perform a rollback is prohibited."
    
  3783.         )
    
  3784.         with atomic(), connection.schema_editor() as editor:
    
  3785.             with self.assertRaisesMessage(TransactionManagementError, message):
    
  3786.                 editor.execute(
    
  3787.                     editor.sql_create_table % {"table": "foo", "definition": ""}
    
  3788.                 )
    
  3789. 
    
  3790.     @skipUnlessDBFeature("supports_foreign_keys", "indexes_foreign_keys")
    
  3791.     def test_foreign_key_index_long_names_regression(self):
    
  3792.         """
    
  3793.         Regression test for #21497.
    
  3794.         Only affects databases that supports foreign keys.
    
  3795.         """
    
  3796.         # Create the table
    
  3797.         with connection.schema_editor() as editor:
    
  3798.             editor.create_model(AuthorWithEvenLongerName)
    
  3799.             editor.create_model(BookWithLongName)
    
  3800.         # Find the properly shortened column name
    
  3801.         column_name = connection.ops.quote_name(
    
  3802.             "author_foreign_key_with_really_long_field_name_id"
    
  3803.         )
    
  3804.         column_name = column_name[1:-1].lower()  # unquote, and, for Oracle, un-upcase
    
  3805.         # Ensure the table is there and has an index on the column
    
  3806.         self.assertIn(
    
  3807.             column_name,
    
  3808.             self.get_indexes(BookWithLongName._meta.db_table),
    
  3809.         )
    
  3810. 
    
  3811.     @skipUnlessDBFeature("supports_foreign_keys")
    
  3812.     def test_add_foreign_key_long_names(self):
    
  3813.         """
    
  3814.         Regression test for #23009.
    
  3815.         Only affects databases that supports foreign keys.
    
  3816.         """
    
  3817.         # Create the initial tables
    
  3818.         with connection.schema_editor() as editor:
    
  3819.             editor.create_model(AuthorWithEvenLongerName)
    
  3820.             editor.create_model(BookWithLongName)
    
  3821.         # Add a second FK, this would fail due to long ref name before the fix
    
  3822.         new_field = ForeignKey(
    
  3823.             AuthorWithEvenLongerName, CASCADE, related_name="something"
    
  3824.         )
    
  3825.         new_field.set_attributes_from_name(
    
  3826.             "author_other_really_long_named_i_mean_so_long_fk"
    
  3827.         )
    
  3828.         with connection.schema_editor() as editor:
    
  3829.             editor.add_field(BookWithLongName, new_field)
    
  3830. 
    
  3831.     @isolate_apps("schema")
    
  3832.     @skipUnlessDBFeature("supports_foreign_keys")
    
  3833.     def test_add_foreign_key_quoted_db_table(self):
    
  3834.         class Author(Model):
    
  3835.             class Meta:
    
  3836.                 db_table = '"table_author_double_quoted"'
    
  3837.                 app_label = "schema"
    
  3838. 
    
  3839.         class Book(Model):
    
  3840.             author = ForeignKey(Author, CASCADE)
    
  3841. 
    
  3842.             class Meta:
    
  3843.                 app_label = "schema"
    
  3844. 
    
  3845.         with connection.schema_editor() as editor:
    
  3846.             editor.create_model(Author)
    
  3847.             editor.create_model(Book)
    
  3848.         self.isolated_local_models = [Author]
    
  3849.         if connection.vendor == "mysql":
    
  3850.             self.assertForeignKeyExists(
    
  3851.                 Book, "author_id", '"table_author_double_quoted"'
    
  3852.             )
    
  3853.         else:
    
  3854.             self.assertForeignKeyExists(Book, "author_id", "table_author_double_quoted")
    
  3855. 
    
  3856.     def test_add_foreign_object(self):
    
  3857.         with connection.schema_editor() as editor:
    
  3858.             editor.create_model(BookForeignObj)
    
  3859.         self.local_models = [BookForeignObj]
    
  3860. 
    
  3861.         new_field = ForeignObject(
    
  3862.             Author, on_delete=CASCADE, from_fields=["author_id"], to_fields=["id"]
    
  3863.         )
    
  3864.         new_field.set_attributes_from_name("author")
    
  3865.         with connection.schema_editor() as editor:
    
  3866.             editor.add_field(BookForeignObj, new_field)
    
  3867. 
    
  3868.     def test_creation_deletion_reserved_names(self):
    
  3869.         """
    
  3870.         Tries creating a model's table, and then deleting it when it has a
    
  3871.         SQL reserved name.
    
  3872.         """
    
  3873.         # Create the table
    
  3874.         with connection.schema_editor() as editor:
    
  3875.             try:
    
  3876.                 editor.create_model(Thing)
    
  3877.             except OperationalError as e:
    
  3878.                 self.fail(
    
  3879.                     "Errors when applying initial migration for a model "
    
  3880.                     "with a table named after an SQL reserved word: %s" % e
    
  3881.                 )
    
  3882.         # The table is there
    
  3883.         list(Thing.objects.all())
    
  3884.         # Clean up that table
    
  3885.         with connection.schema_editor() as editor:
    
  3886.             editor.delete_model(Thing)
    
  3887.         # The table is gone
    
  3888.         with self.assertRaises(DatabaseError):
    
  3889.             list(Thing.objects.all())
    
  3890. 
    
  3891.     def test_remove_constraints_capital_letters(self):
    
  3892.         """
    
  3893.         #23065 - Constraint names must be quoted if they contain capital letters.
    
  3894.         """
    
  3895. 
    
  3896.         def get_field(*args, field_class=IntegerField, **kwargs):
    
  3897.             kwargs["db_column"] = "CamelCase"
    
  3898.             field = field_class(*args, **kwargs)
    
  3899.             field.set_attributes_from_name("CamelCase")
    
  3900.             return field
    
  3901. 
    
  3902.         model = Author
    
  3903.         field = get_field()
    
  3904.         table = model._meta.db_table
    
  3905.         column = field.column
    
  3906.         identifier_converter = connection.introspection.identifier_converter
    
  3907. 
    
  3908.         with connection.schema_editor() as editor:
    
  3909.             editor.create_model(model)
    
  3910.             editor.add_field(model, field)
    
  3911. 
    
  3912.             constraint_name = "CamelCaseIndex"
    
  3913.             expected_constraint_name = identifier_converter(constraint_name)
    
  3914.             editor.execute(
    
  3915.                 editor.sql_create_index
    
  3916.                 % {
    
  3917.                     "table": editor.quote_name(table),
    
  3918.                     "name": editor.quote_name(constraint_name),
    
  3919.                     "using": "",
    
  3920.                     "columns": editor.quote_name(column),
    
  3921.                     "extra": "",
    
  3922.                     "condition": "",
    
  3923.                     "include": "",
    
  3924.                 }
    
  3925.             )
    
  3926.             self.assertIn(
    
  3927.                 expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3928.             )
    
  3929.             editor.alter_field(model, get_field(db_index=True), field, strict=True)
    
  3930.             self.assertNotIn(
    
  3931.                 expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3932.             )
    
  3933. 
    
  3934.             constraint_name = "CamelCaseUniqConstraint"
    
  3935.             expected_constraint_name = identifier_converter(constraint_name)
    
  3936.             editor.execute(editor._create_unique_sql(model, [field], constraint_name))
    
  3937.             self.assertIn(
    
  3938.                 expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3939.             )
    
  3940.             editor.alter_field(model, get_field(unique=True), field, strict=True)
    
  3941.             self.assertNotIn(
    
  3942.                 expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3943.             )
    
  3944. 
    
  3945.             if editor.sql_create_fk and connection.features.can_introspect_foreign_keys:
    
  3946.                 constraint_name = "CamelCaseFKConstraint"
    
  3947.                 expected_constraint_name = identifier_converter(constraint_name)
    
  3948.                 editor.execute(
    
  3949.                     editor.sql_create_fk
    
  3950.                     % {
    
  3951.                         "table": editor.quote_name(table),
    
  3952.                         "name": editor.quote_name(constraint_name),
    
  3953.                         "column": editor.quote_name(column),
    
  3954.                         "to_table": editor.quote_name(table),
    
  3955.                         "to_column": editor.quote_name(model._meta.auto_field.column),
    
  3956.                         "deferrable": connection.ops.deferrable_sql(),
    
  3957.                     }
    
  3958.                 )
    
  3959.                 self.assertIn(
    
  3960.                     expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3961.                 )
    
  3962.                 editor.alter_field(
    
  3963.                     model,
    
  3964.                     get_field(Author, CASCADE, field_class=ForeignKey),
    
  3965.                     field,
    
  3966.                     strict=True,
    
  3967.                 )
    
  3968.                 self.assertNotIn(
    
  3969.                     expected_constraint_name, self.get_constraints(model._meta.db_table)
    
  3970.                 )
    
  3971. 
    
  3972.     def test_add_field_use_effective_default(self):
    
  3973.         """
    
  3974.         #23987 - effective_default() should be used as the field default when
    
  3975.         adding a new field.
    
  3976.         """
    
  3977.         # Create the table
    
  3978.         with connection.schema_editor() as editor:
    
  3979.             editor.create_model(Author)
    
  3980.         # Ensure there's no surname field
    
  3981.         columns = self.column_classes(Author)
    
  3982.         self.assertNotIn("surname", columns)
    
  3983.         # Create a row
    
  3984.         Author.objects.create(name="Anonymous1")
    
  3985.         # Add new CharField to ensure default will be used from effective_default
    
  3986.         new_field = CharField(max_length=15, blank=True)
    
  3987.         new_field.set_attributes_from_name("surname")
    
  3988.         with connection.schema_editor() as editor:
    
  3989.             editor.add_field(Author, new_field)
    
  3990.         # Ensure field was added with the right default
    
  3991.         with connection.cursor() as cursor:
    
  3992.             cursor.execute("SELECT surname FROM schema_author;")
    
  3993.             item = cursor.fetchall()[0]
    
  3994.             self.assertEqual(
    
  3995.                 item[0],
    
  3996.                 None if connection.features.interprets_empty_strings_as_nulls else "",
    
  3997.             )
    
  3998. 
    
  3999.     def test_add_field_default_dropped(self):
    
  4000.         # Create the table
    
  4001.         with connection.schema_editor() as editor:
    
  4002.             editor.create_model(Author)
    
  4003.         # Ensure there's no surname field
    
  4004.         columns = self.column_classes(Author)
    
  4005.         self.assertNotIn("surname", columns)
    
  4006.         # Create a row
    
  4007.         Author.objects.create(name="Anonymous1")
    
  4008.         # Add new CharField with a default
    
  4009.         new_field = CharField(max_length=15, blank=True, default="surname default")
    
  4010.         new_field.set_attributes_from_name("surname")
    
  4011.         with connection.schema_editor() as editor:
    
  4012.             editor.add_field(Author, new_field)
    
  4013.         # Ensure field was added with the right default
    
  4014.         with connection.cursor() as cursor:
    
  4015.             cursor.execute("SELECT surname FROM schema_author;")
    
  4016.             item = cursor.fetchall()[0]
    
  4017.             self.assertEqual(item[0], "surname default")
    
  4018.             # And that the default is no longer set in the database.
    
  4019.             field = next(
    
  4020.                 f
    
  4021.                 for f in connection.introspection.get_table_description(
    
  4022.                     cursor, "schema_author"
    
  4023.                 )
    
  4024.                 if f.name == "surname"
    
  4025.             )
    
  4026.             if connection.features.can_introspect_default:
    
  4027.                 self.assertIsNone(field.default)
    
  4028. 
    
  4029.     def test_add_field_default_nullable(self):
    
  4030.         with connection.schema_editor() as editor:
    
  4031.             editor.create_model(Author)
    
  4032.         # Add new nullable CharField with a default.
    
  4033.         new_field = CharField(max_length=15, blank=True, null=True, default="surname")
    
  4034.         new_field.set_attributes_from_name("surname")
    
  4035.         with connection.schema_editor() as editor:
    
  4036.             editor.add_field(Author, new_field)
    
  4037.         Author.objects.create(name="Anonymous1")
    
  4038.         with connection.cursor() as cursor:
    
  4039.             cursor.execute("SELECT surname FROM schema_author;")
    
  4040.             item = cursor.fetchall()[0]
    
  4041.             self.assertIsNone(item[0])
    
  4042.             field = next(
    
  4043.                 f
    
  4044.                 for f in connection.introspection.get_table_description(
    
  4045.                     cursor,
    
  4046.                     "schema_author",
    
  4047.                 )
    
  4048.                 if f.name == "surname"
    
  4049.             )
    
  4050.             # Field is still nullable.
    
  4051.             self.assertTrue(field.null_ok)
    
  4052.             # The database default is no longer set.
    
  4053.             if connection.features.can_introspect_default:
    
  4054.                 self.assertIn(field.default, ["NULL", None])
    
  4055. 
    
  4056.     def test_add_textfield_default_nullable(self):
    
  4057.         with connection.schema_editor() as editor:
    
  4058.             editor.create_model(Author)
    
  4059.         # Add new nullable TextField with a default.
    
  4060.         new_field = TextField(blank=True, null=True, default="text")
    
  4061.         new_field.set_attributes_from_name("description")
    
  4062.         with connection.schema_editor() as editor:
    
  4063.             editor.add_field(Author, new_field)
    
  4064.         Author.objects.create(name="Anonymous1")
    
  4065.         with connection.cursor() as cursor:
    
  4066.             cursor.execute("SELECT description FROM schema_author;")
    
  4067.             item = cursor.fetchall()[0]
    
  4068.             self.assertIsNone(item[0])
    
  4069.             field = next(
    
  4070.                 f
    
  4071.                 for f in connection.introspection.get_table_description(
    
  4072.                     cursor,
    
  4073.                     "schema_author",
    
  4074.                 )
    
  4075.                 if f.name == "description"
    
  4076.             )
    
  4077.             # Field is still nullable.
    
  4078.             self.assertTrue(field.null_ok)
    
  4079.             # The database default is no longer set.
    
  4080.             if connection.features.can_introspect_default:
    
  4081.                 self.assertIn(field.default, ["NULL", None])
    
  4082. 
    
  4083.     def test_alter_field_default_dropped(self):
    
  4084.         # Create the table
    
  4085.         with connection.schema_editor() as editor:
    
  4086.             editor.create_model(Author)
    
  4087.         # Create a row
    
  4088.         Author.objects.create(name="Anonymous1")
    
  4089.         self.assertIsNone(Author.objects.get().height)
    
  4090.         old_field = Author._meta.get_field("height")
    
  4091.         # The default from the new field is used in updating existing rows.
    
  4092.         new_field = IntegerField(blank=True, default=42)
    
  4093.         new_field.set_attributes_from_name("height")
    
  4094.         with connection.schema_editor() as editor:
    
  4095.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4096.         self.assertEqual(Author.objects.get().height, 42)
    
  4097.         # The database default should be removed.
    
  4098.         with connection.cursor() as cursor:
    
  4099.             field = next(
    
  4100.                 f
    
  4101.                 for f in connection.introspection.get_table_description(
    
  4102.                     cursor, "schema_author"
    
  4103.                 )
    
  4104.                 if f.name == "height"
    
  4105.             )
    
  4106.             if connection.features.can_introspect_default:
    
  4107.                 self.assertIsNone(field.default)
    
  4108. 
    
  4109.     def test_alter_field_default_doesnt_perform_queries(self):
    
  4110.         """
    
  4111.         No queries are performed if a field default changes and the field's
    
  4112.         not changing from null to non-null.
    
  4113.         """
    
  4114.         with connection.schema_editor() as editor:
    
  4115.             editor.create_model(AuthorWithDefaultHeight)
    
  4116.         old_field = AuthorWithDefaultHeight._meta.get_field("height")
    
  4117.         new_default = old_field.default * 2
    
  4118.         new_field = PositiveIntegerField(null=True, blank=True, default=new_default)
    
  4119.         new_field.set_attributes_from_name("height")
    
  4120.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  4121.             editor.alter_field(
    
  4122.                 AuthorWithDefaultHeight, old_field, new_field, strict=True
    
  4123.             )
    
  4124. 
    
  4125.     @skipUnlessDBFeature("supports_foreign_keys")
    
  4126.     def test_alter_field_fk_attributes_noop(self):
    
  4127.         """
    
  4128.         No queries are performed when changing field attributes that don't
    
  4129.         affect the schema.
    
  4130.         """
    
  4131.         with connection.schema_editor() as editor:
    
  4132.             editor.create_model(Author)
    
  4133.             editor.create_model(Book)
    
  4134.         old_field = Book._meta.get_field("author")
    
  4135.         new_field = ForeignKey(
    
  4136.             Author,
    
  4137.             blank=True,
    
  4138.             editable=False,
    
  4139.             error_messages={"invalid": "error message"},
    
  4140.             help_text="help text",
    
  4141.             limit_choices_to={"limit": "choice"},
    
  4142.             on_delete=PROTECT,
    
  4143.             related_name="related_name",
    
  4144.             related_query_name="related_query_name",
    
  4145.             validators=[lambda x: x],
    
  4146.             verbose_name="verbose name",
    
  4147.         )
    
  4148.         new_field.set_attributes_from_name("author")
    
  4149.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  4150.             editor.alter_field(Book, old_field, new_field, strict=True)
    
  4151.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  4152.             editor.alter_field(Book, new_field, old_field, strict=True)
    
  4153. 
    
  4154.     def test_alter_field_choices_noop(self):
    
  4155.         with connection.schema_editor() as editor:
    
  4156.             editor.create_model(Author)
    
  4157.         old_field = Author._meta.get_field("name")
    
  4158.         new_field = CharField(
    
  4159.             choices=(("Jane", "Jane"), ("Joe", "Joe")),
    
  4160.             max_length=255,
    
  4161.         )
    
  4162.         new_field.set_attributes_from_name("name")
    
  4163.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  4164.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4165.         with connection.schema_editor() as editor, self.assertNumQueries(0):
    
  4166.             editor.alter_field(Author, new_field, old_field, strict=True)
    
  4167. 
    
  4168.     def test_add_textfield_unhashable_default(self):
    
  4169.         # Create the table
    
  4170.         with connection.schema_editor() as editor:
    
  4171.             editor.create_model(Author)
    
  4172.         # Create a row
    
  4173.         Author.objects.create(name="Anonymous1")
    
  4174.         # Create a field that has an unhashable default
    
  4175.         new_field = TextField(default={})
    
  4176.         new_field.set_attributes_from_name("info")
    
  4177.         with connection.schema_editor() as editor:
    
  4178.             editor.add_field(Author, new_field)
    
  4179. 
    
  4180.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4181.     def test_add_indexed_charfield(self):
    
  4182.         field = CharField(max_length=255, db_index=True)
    
  4183.         field.set_attributes_from_name("nom_de_plume")
    
  4184.         with connection.schema_editor() as editor:
    
  4185.             editor.create_model(Author)
    
  4186.             editor.add_field(Author, field)
    
  4187.         # Should create two indexes; one for like operator.
    
  4188.         self.assertEqual(
    
  4189.             self.get_constraints_for_column(Author, "nom_de_plume"),
    
  4190.             [
    
  4191.                 "schema_author_nom_de_plume_7570a851",
    
  4192.                 "schema_author_nom_de_plume_7570a851_like",
    
  4193.             ],
    
  4194.         )
    
  4195. 
    
  4196.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4197.     def test_add_unique_charfield(self):
    
  4198.         field = CharField(max_length=255, unique=True)
    
  4199.         field.set_attributes_from_name("nom_de_plume")
    
  4200.         with connection.schema_editor() as editor:
    
  4201.             editor.create_model(Author)
    
  4202.             editor.add_field(Author, field)
    
  4203.         # Should create two indexes; one for like operator.
    
  4204.         self.assertEqual(
    
  4205.             self.get_constraints_for_column(Author, "nom_de_plume"),
    
  4206.             [
    
  4207.                 "schema_author_nom_de_plume_7570a851_like",
    
  4208.                 "schema_author_nom_de_plume_key",
    
  4209.             ],
    
  4210.         )
    
  4211. 
    
  4212.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4213.     def test_alter_field_add_index_to_charfield(self):
    
  4214.         # Create the table and verify no initial indexes.
    
  4215.         with connection.schema_editor() as editor:
    
  4216.             editor.create_model(Author)
    
  4217.         self.assertEqual(self.get_constraints_for_column(Author, "name"), [])
    
  4218.         # Alter to add db_index=True and create 2 indexes.
    
  4219.         old_field = Author._meta.get_field("name")
    
  4220.         new_field = CharField(max_length=255, db_index=True)
    
  4221.         new_field.set_attributes_from_name("name")
    
  4222.         with connection.schema_editor() as editor:
    
  4223.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4224.         self.assertEqual(
    
  4225.             self.get_constraints_for_column(Author, "name"),
    
  4226.             ["schema_author_name_1fbc5617", "schema_author_name_1fbc5617_like"],
    
  4227.         )
    
  4228.         # Remove db_index=True to drop both indexes.
    
  4229.         with connection.schema_editor() as editor:
    
  4230.             editor.alter_field(Author, new_field, old_field, strict=True)
    
  4231.         self.assertEqual(self.get_constraints_for_column(Author, "name"), [])
    
  4232. 
    
  4233.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4234.     def test_alter_field_add_unique_to_charfield(self):
    
  4235.         # Create the table and verify no initial indexes.
    
  4236.         with connection.schema_editor() as editor:
    
  4237.             editor.create_model(Author)
    
  4238.         self.assertEqual(self.get_constraints_for_column(Author, "name"), [])
    
  4239.         # Alter to add unique=True and create 2 indexes.
    
  4240.         old_field = Author._meta.get_field("name")
    
  4241.         new_field = CharField(max_length=255, unique=True)
    
  4242.         new_field.set_attributes_from_name("name")
    
  4243.         with connection.schema_editor() as editor:
    
  4244.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4245.         self.assertEqual(
    
  4246.             self.get_constraints_for_column(Author, "name"),
    
  4247.             ["schema_author_name_1fbc5617_like", "schema_author_name_1fbc5617_uniq"],
    
  4248.         )
    
  4249.         # Remove unique=True to drop both indexes.
    
  4250.         with connection.schema_editor() as editor:
    
  4251.             editor.alter_field(Author, new_field, old_field, strict=True)
    
  4252.         self.assertEqual(self.get_constraints_for_column(Author, "name"), [])
    
  4253. 
    
  4254.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4255.     def test_alter_field_add_index_to_textfield(self):
    
  4256.         # Create the table and verify no initial indexes.
    
  4257.         with connection.schema_editor() as editor:
    
  4258.             editor.create_model(Note)
    
  4259.         self.assertEqual(self.get_constraints_for_column(Note, "info"), [])
    
  4260.         # Alter to add db_index=True and create 2 indexes.
    
  4261.         old_field = Note._meta.get_field("info")
    
  4262.         new_field = TextField(db_index=True)
    
  4263.         new_field.set_attributes_from_name("info")
    
  4264.         with connection.schema_editor() as editor:
    
  4265.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  4266.         self.assertEqual(
    
  4267.             self.get_constraints_for_column(Note, "info"),
    
  4268.             ["schema_note_info_4b0ea695", "schema_note_info_4b0ea695_like"],
    
  4269.         )
    
  4270.         # Remove db_index=True to drop both indexes.
    
  4271.         with connection.schema_editor() as editor:
    
  4272.             editor.alter_field(Note, new_field, old_field, strict=True)
    
  4273.         self.assertEqual(self.get_constraints_for_column(Note, "info"), [])
    
  4274. 
    
  4275.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4276.     def test_alter_field_add_unique_to_charfield_with_db_index(self):
    
  4277.         # Create the table and verify initial indexes.
    
  4278.         with connection.schema_editor() as editor:
    
  4279.             editor.create_model(BookWithoutAuthor)
    
  4280.         self.assertEqual(
    
  4281.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4282.             ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"],
    
  4283.         )
    
  4284.         # Alter to add unique=True (should replace the index)
    
  4285.         old_field = BookWithoutAuthor._meta.get_field("title")
    
  4286.         new_field = CharField(max_length=100, db_index=True, unique=True)
    
  4287.         new_field.set_attributes_from_name("title")
    
  4288.         with connection.schema_editor() as editor:
    
  4289.             editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
    
  4290.         self.assertEqual(
    
  4291.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4292.             ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"],
    
  4293.         )
    
  4294.         # Alter to remove unique=True (should drop unique index)
    
  4295.         new_field2 = CharField(max_length=100, db_index=True)
    
  4296.         new_field2.set_attributes_from_name("title")
    
  4297.         with connection.schema_editor() as editor:
    
  4298.             editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
    
  4299.         self.assertEqual(
    
  4300.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4301.             ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"],
    
  4302.         )
    
  4303. 
    
  4304.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4305.     def test_alter_field_remove_unique_and_db_index_from_charfield(self):
    
  4306.         # Create the table and verify initial indexes.
    
  4307.         with connection.schema_editor() as editor:
    
  4308.             editor.create_model(BookWithoutAuthor)
    
  4309.         self.assertEqual(
    
  4310.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4311.             ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"],
    
  4312.         )
    
  4313.         # Alter to add unique=True (should replace the index)
    
  4314.         old_field = BookWithoutAuthor._meta.get_field("title")
    
  4315.         new_field = CharField(max_length=100, db_index=True, unique=True)
    
  4316.         new_field.set_attributes_from_name("title")
    
  4317.         with connection.schema_editor() as editor:
    
  4318.             editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
    
  4319.         self.assertEqual(
    
  4320.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4321.             ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"],
    
  4322.         )
    
  4323.         # Alter to remove both unique=True and db_index=True (should drop all indexes)
    
  4324.         new_field2 = CharField(max_length=100)
    
  4325.         new_field2.set_attributes_from_name("title")
    
  4326.         with connection.schema_editor() as editor:
    
  4327.             editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
    
  4328.         self.assertEqual(
    
  4329.             self.get_constraints_for_column(BookWithoutAuthor, "title"), []
    
  4330.         )
    
  4331. 
    
  4332.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4333.     def test_alter_field_swap_unique_and_db_index_with_charfield(self):
    
  4334.         # Create the table and verify initial indexes.
    
  4335.         with connection.schema_editor() as editor:
    
  4336.             editor.create_model(BookWithoutAuthor)
    
  4337.         self.assertEqual(
    
  4338.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4339.             ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"],
    
  4340.         )
    
  4341.         # Alter to set unique=True and remove db_index=True (should replace the index)
    
  4342.         old_field = BookWithoutAuthor._meta.get_field("title")
    
  4343.         new_field = CharField(max_length=100, unique=True)
    
  4344.         new_field.set_attributes_from_name("title")
    
  4345.         with connection.schema_editor() as editor:
    
  4346.             editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
    
  4347.         self.assertEqual(
    
  4348.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4349.             ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"],
    
  4350.         )
    
  4351.         # Alter to set db_index=True and remove unique=True (should restore index)
    
  4352.         new_field2 = CharField(max_length=100, db_index=True)
    
  4353.         new_field2.set_attributes_from_name("title")
    
  4354.         with connection.schema_editor() as editor:
    
  4355.             editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
    
  4356.         self.assertEqual(
    
  4357.             self.get_constraints_for_column(BookWithoutAuthor, "title"),
    
  4358.             ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"],
    
  4359.         )
    
  4360. 
    
  4361.     @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific")
    
  4362.     def test_alter_field_add_db_index_to_charfield_with_unique(self):
    
  4363.         # Create the table and verify initial indexes.
    
  4364.         with connection.schema_editor() as editor:
    
  4365.             editor.create_model(Tag)
    
  4366.         self.assertEqual(
    
  4367.             self.get_constraints_for_column(Tag, "slug"),
    
  4368.             ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"],
    
  4369.         )
    
  4370.         # Alter to add db_index=True
    
  4371.         old_field = Tag._meta.get_field("slug")
    
  4372.         new_field = SlugField(db_index=True, unique=True)
    
  4373.         new_field.set_attributes_from_name("slug")
    
  4374.         with connection.schema_editor() as editor:
    
  4375.             editor.alter_field(Tag, old_field, new_field, strict=True)
    
  4376.         self.assertEqual(
    
  4377.             self.get_constraints_for_column(Tag, "slug"),
    
  4378.             ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"],
    
  4379.         )
    
  4380.         # Alter to remove db_index=True
    
  4381.         new_field2 = SlugField(unique=True)
    
  4382.         new_field2.set_attributes_from_name("slug")
    
  4383.         with connection.schema_editor() as editor:
    
  4384.             editor.alter_field(Tag, new_field, new_field2, strict=True)
    
  4385.         self.assertEqual(
    
  4386.             self.get_constraints_for_column(Tag, "slug"),
    
  4387.             ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"],
    
  4388.         )
    
  4389. 
    
  4390.     def test_alter_field_add_index_to_integerfield(self):
    
  4391.         # Create the table and verify no initial indexes.
    
  4392.         with connection.schema_editor() as editor:
    
  4393.             editor.create_model(Author)
    
  4394.         self.assertEqual(self.get_constraints_for_column(Author, "weight"), [])
    
  4395. 
    
  4396.         # Alter to add db_index=True and create index.
    
  4397.         old_field = Author._meta.get_field("weight")
    
  4398.         new_field = IntegerField(null=True, db_index=True)
    
  4399.         new_field.set_attributes_from_name("weight")
    
  4400.         with connection.schema_editor() as editor:
    
  4401.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4402.         self.assertEqual(
    
  4403.             self.get_constraints_for_column(Author, "weight"),
    
  4404.             ["schema_author_weight_587740f9"],
    
  4405.         )
    
  4406. 
    
  4407.         # Remove db_index=True to drop index.
    
  4408.         with connection.schema_editor() as editor:
    
  4409.             editor.alter_field(Author, new_field, old_field, strict=True)
    
  4410.         self.assertEqual(self.get_constraints_for_column(Author, "weight"), [])
    
  4411. 
    
  4412.     def test_alter_pk_with_self_referential_field(self):
    
  4413.         """
    
  4414.         Changing the primary key field name of a model with a self-referential
    
  4415.         foreign key (#26384).
    
  4416.         """
    
  4417.         with connection.schema_editor() as editor:
    
  4418.             editor.create_model(Node)
    
  4419.         old_field = Node._meta.get_field("node_id")
    
  4420.         new_field = AutoField(primary_key=True)
    
  4421.         new_field.set_attributes_from_name("id")
    
  4422.         with connection.schema_editor() as editor:
    
  4423.             editor.alter_field(Node, old_field, new_field, strict=True)
    
  4424.         self.assertForeignKeyExists(Node, "parent_id", Node._meta.db_table)
    
  4425. 
    
  4426.     @mock.patch("django.db.backends.base.schema.datetime")
    
  4427.     @mock.patch("django.db.backends.base.schema.timezone")
    
  4428.     def test_add_datefield_and_datetimefield_use_effective_default(
    
  4429.         self, mocked_datetime, mocked_tz
    
  4430.     ):
    
  4431.         """
    
  4432.         effective_default() should be used for DateField, DateTimeField, and
    
  4433.         TimeField if auto_now or auto_now_add is set (#25005).
    
  4434.         """
    
  4435.         now = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1)
    
  4436.         now_tz = datetime.datetime(
    
  4437.             month=1, day=1, year=2000, hour=1, minute=1, tzinfo=datetime.timezone.utc
    
  4438.         )
    
  4439.         mocked_datetime.now = mock.MagicMock(return_value=now)
    
  4440.         mocked_tz.now = mock.MagicMock(return_value=now_tz)
    
  4441.         # Create the table
    
  4442.         with connection.schema_editor() as editor:
    
  4443.             editor.create_model(Author)
    
  4444.         # Check auto_now/auto_now_add attributes are not defined
    
  4445.         columns = self.column_classes(Author)
    
  4446.         self.assertNotIn("dob_auto_now", columns)
    
  4447.         self.assertNotIn("dob_auto_now_add", columns)
    
  4448.         self.assertNotIn("dtob_auto_now", columns)
    
  4449.         self.assertNotIn("dtob_auto_now_add", columns)
    
  4450.         self.assertNotIn("tob_auto_now", columns)
    
  4451.         self.assertNotIn("tob_auto_now_add", columns)
    
  4452.         # Create a row
    
  4453.         Author.objects.create(name="Anonymous1")
    
  4454.         # Ensure fields were added with the correct defaults
    
  4455.         dob_auto_now = DateField(auto_now=True)
    
  4456.         dob_auto_now.set_attributes_from_name("dob_auto_now")
    
  4457.         self.check_added_field_default(
    
  4458.             editor,
    
  4459.             Author,
    
  4460.             dob_auto_now,
    
  4461.             "dob_auto_now",
    
  4462.             now.date(),
    
  4463.             cast_function=lambda x: x.date(),
    
  4464.         )
    
  4465.         dob_auto_now_add = DateField(auto_now_add=True)
    
  4466.         dob_auto_now_add.set_attributes_from_name("dob_auto_now_add")
    
  4467.         self.check_added_field_default(
    
  4468.             editor,
    
  4469.             Author,
    
  4470.             dob_auto_now_add,
    
  4471.             "dob_auto_now_add",
    
  4472.             now.date(),
    
  4473.             cast_function=lambda x: x.date(),
    
  4474.         )
    
  4475.         dtob_auto_now = DateTimeField(auto_now=True)
    
  4476.         dtob_auto_now.set_attributes_from_name("dtob_auto_now")
    
  4477.         self.check_added_field_default(
    
  4478.             editor,
    
  4479.             Author,
    
  4480.             dtob_auto_now,
    
  4481.             "dtob_auto_now",
    
  4482.             now,
    
  4483.         )
    
  4484.         dt_tm_of_birth_auto_now_add = DateTimeField(auto_now_add=True)
    
  4485.         dt_tm_of_birth_auto_now_add.set_attributes_from_name("dtob_auto_now_add")
    
  4486.         self.check_added_field_default(
    
  4487.             editor,
    
  4488.             Author,
    
  4489.             dt_tm_of_birth_auto_now_add,
    
  4490.             "dtob_auto_now_add",
    
  4491.             now,
    
  4492.         )
    
  4493.         tob_auto_now = TimeField(auto_now=True)
    
  4494.         tob_auto_now.set_attributes_from_name("tob_auto_now")
    
  4495.         self.check_added_field_default(
    
  4496.             editor,
    
  4497.             Author,
    
  4498.             tob_auto_now,
    
  4499.             "tob_auto_now",
    
  4500.             now.time(),
    
  4501.             cast_function=lambda x: x.time(),
    
  4502.         )
    
  4503.         tob_auto_now_add = TimeField(auto_now_add=True)
    
  4504.         tob_auto_now_add.set_attributes_from_name("tob_auto_now_add")
    
  4505.         self.check_added_field_default(
    
  4506.             editor,
    
  4507.             Author,
    
  4508.             tob_auto_now_add,
    
  4509.             "tob_auto_now_add",
    
  4510.             now.time(),
    
  4511.             cast_function=lambda x: x.time(),
    
  4512.         )
    
  4513. 
    
  4514.     def test_namespaced_db_table_create_index_name(self):
    
  4515.         """
    
  4516.         Table names are stripped of their namespace/schema before being used to
    
  4517.         generate index names.
    
  4518.         """
    
  4519.         with connection.schema_editor() as editor:
    
  4520.             max_name_length = connection.ops.max_name_length() or 200
    
  4521.             namespace = "n" * max_name_length
    
  4522.             table_name = "t" * max_name_length
    
  4523.             namespaced_table_name = '"%s"."%s"' % (namespace, table_name)
    
  4524.             self.assertEqual(
    
  4525.                 editor._create_index_name(table_name, []),
    
  4526.                 editor._create_index_name(namespaced_table_name, []),
    
  4527.             )
    
  4528. 
    
  4529.     @unittest.skipUnless(
    
  4530.         connection.vendor == "oracle", "Oracle specific db_table syntax"
    
  4531.     )
    
  4532.     def test_creation_with_db_table_double_quotes(self):
    
  4533.         oracle_user = connection.creation._test_database_user()
    
  4534. 
    
  4535.         class Student(Model):
    
  4536.             name = CharField(max_length=30)
    
  4537. 
    
  4538.             class Meta:
    
  4539.                 app_label = "schema"
    
  4540.                 apps = new_apps
    
  4541.                 db_table = '"%s"."DJANGO_STUDENT_TABLE"' % oracle_user
    
  4542. 
    
  4543.         class Document(Model):
    
  4544.             name = CharField(max_length=30)
    
  4545.             students = ManyToManyField(Student)
    
  4546. 
    
  4547.             class Meta:
    
  4548.                 app_label = "schema"
    
  4549.                 apps = new_apps
    
  4550.                 db_table = '"%s"."DJANGO_DOCUMENT_TABLE"' % oracle_user
    
  4551. 
    
  4552.         self.isolated_local_models = [Student, Document]
    
  4553. 
    
  4554.         with connection.schema_editor() as editor:
    
  4555.             editor.create_model(Student)
    
  4556.             editor.create_model(Document)
    
  4557. 
    
  4558.         doc = Document.objects.create(name="Test Name")
    
  4559.         student = Student.objects.create(name="Some man")
    
  4560.         doc.students.add(student)
    
  4561. 
    
  4562.     @isolate_apps("schema")
    
  4563.     @unittest.skipUnless(
    
  4564.         connection.vendor == "postgresql", "PostgreSQL specific db_table syntax."
    
  4565.     )
    
  4566.     def test_namespaced_db_table_foreign_key_reference(self):
    
  4567.         with connection.cursor() as cursor:
    
  4568.             cursor.execute("CREATE SCHEMA django_schema_tests")
    
  4569. 
    
  4570.         def delete_schema():
    
  4571.             with connection.cursor() as cursor:
    
  4572.                 cursor.execute("DROP SCHEMA django_schema_tests CASCADE")
    
  4573. 
    
  4574.         self.addCleanup(delete_schema)
    
  4575. 
    
  4576.         class Author(Model):
    
  4577.             class Meta:
    
  4578.                 app_label = "schema"
    
  4579. 
    
  4580.         class Book(Model):
    
  4581.             class Meta:
    
  4582.                 app_label = "schema"
    
  4583.                 db_table = '"django_schema_tests"."schema_book"'
    
  4584. 
    
  4585.         author = ForeignKey(Author, CASCADE)
    
  4586.         author.set_attributes_from_name("author")
    
  4587. 
    
  4588.         with connection.schema_editor() as editor:
    
  4589.             editor.create_model(Author)
    
  4590.             editor.create_model(Book)
    
  4591.             editor.add_field(Book, author)
    
  4592. 
    
  4593.     def test_rename_table_renames_deferred_sql_references(self):
    
  4594.         atomic_rename = connection.features.supports_atomic_references_rename
    
  4595.         with connection.schema_editor(atomic=atomic_rename) as editor:
    
  4596.             editor.create_model(Author)
    
  4597.             editor.create_model(Book)
    
  4598.             editor.alter_db_table(Author, "schema_author", "schema_renamed_author")
    
  4599.             editor.alter_db_table(Author, "schema_book", "schema_renamed_book")
    
  4600.             try:
    
  4601.                 self.assertGreater(len(editor.deferred_sql), 0)
    
  4602.                 for statement in editor.deferred_sql:
    
  4603.                     self.assertIs(statement.references_table("schema_author"), False)
    
  4604.                     self.assertIs(statement.references_table("schema_book"), False)
    
  4605.             finally:
    
  4606.                 editor.alter_db_table(Author, "schema_renamed_author", "schema_author")
    
  4607.                 editor.alter_db_table(Author, "schema_renamed_book", "schema_book")
    
  4608. 
    
  4609.     def test_rename_column_renames_deferred_sql_references(self):
    
  4610.         with connection.schema_editor() as editor:
    
  4611.             editor.create_model(Author)
    
  4612.             editor.create_model(Book)
    
  4613.             old_title = Book._meta.get_field("title")
    
  4614.             new_title = CharField(max_length=100, db_index=True)
    
  4615.             new_title.set_attributes_from_name("renamed_title")
    
  4616.             editor.alter_field(Book, old_title, new_title)
    
  4617.             old_author = Book._meta.get_field("author")
    
  4618.             new_author = ForeignKey(Author, CASCADE)
    
  4619.             new_author.set_attributes_from_name("renamed_author")
    
  4620.             editor.alter_field(Book, old_author, new_author)
    
  4621.             self.assertGreater(len(editor.deferred_sql), 0)
    
  4622.             for statement in editor.deferred_sql:
    
  4623.                 self.assertIs(statement.references_column("book", "title"), False)
    
  4624.                 self.assertIs(statement.references_column("book", "author_id"), False)
    
  4625. 
    
  4626.     @isolate_apps("schema")
    
  4627.     def test_referenced_field_without_constraint_rename_inside_atomic_block(self):
    
  4628.         """
    
  4629.         Foreign keys without database level constraint don't prevent the field
    
  4630.         they reference from being renamed in an atomic block.
    
  4631.         """
    
  4632. 
    
  4633.         class Foo(Model):
    
  4634.             field = CharField(max_length=255, unique=True)
    
  4635. 
    
  4636.             class Meta:
    
  4637.                 app_label = "schema"
    
  4638. 
    
  4639.         class Bar(Model):
    
  4640.             foo = ForeignKey(Foo, CASCADE, to_field="field", db_constraint=False)
    
  4641. 
    
  4642.             class Meta:
    
  4643.                 app_label = "schema"
    
  4644. 
    
  4645.         self.isolated_local_models = [Foo, Bar]
    
  4646.         with connection.schema_editor() as editor:
    
  4647.             editor.create_model(Foo)
    
  4648.             editor.create_model(Bar)
    
  4649. 
    
  4650.         new_field = CharField(max_length=255, unique=True)
    
  4651.         new_field.set_attributes_from_name("renamed")
    
  4652.         with connection.schema_editor(atomic=True) as editor:
    
  4653.             editor.alter_field(Foo, Foo._meta.get_field("field"), new_field)
    
  4654. 
    
  4655.     @isolate_apps("schema")
    
  4656.     def test_referenced_table_without_constraint_rename_inside_atomic_block(self):
    
  4657.         """
    
  4658.         Foreign keys without database level constraint don't prevent the table
    
  4659.         they reference from being renamed in an atomic block.
    
  4660.         """
    
  4661. 
    
  4662.         class Foo(Model):
    
  4663.             field = CharField(max_length=255, unique=True)
    
  4664. 
    
  4665.             class Meta:
    
  4666.                 app_label = "schema"
    
  4667. 
    
  4668.         class Bar(Model):
    
  4669.             foo = ForeignKey(Foo, CASCADE, to_field="field", db_constraint=False)
    
  4670. 
    
  4671.             class Meta:
    
  4672.                 app_label = "schema"
    
  4673. 
    
  4674.         self.isolated_local_models = [Foo, Bar]
    
  4675.         with connection.schema_editor() as editor:
    
  4676.             editor.create_model(Foo)
    
  4677.             editor.create_model(Bar)
    
  4678. 
    
  4679.         new_field = CharField(max_length=255, unique=True)
    
  4680.         new_field.set_attributes_from_name("renamed")
    
  4681.         with connection.schema_editor(atomic=True) as editor:
    
  4682.             editor.alter_db_table(Foo, Foo._meta.db_table, "renamed_table")
    
  4683.         Foo._meta.db_table = "renamed_table"
    
  4684. 
    
  4685.     @isolate_apps("schema")
    
  4686.     @skipUnlessDBFeature("supports_collation_on_charfield")
    
  4687.     def test_db_collation_charfield(self):
    
  4688.         collation = connection.features.test_collations.get("non_default")
    
  4689.         if not collation:
    
  4690.             self.skipTest("Language collations are not supported.")
    
  4691. 
    
  4692.         class Foo(Model):
    
  4693.             field = CharField(max_length=255, db_collation=collation)
    
  4694. 
    
  4695.             class Meta:
    
  4696.                 app_label = "schema"
    
  4697. 
    
  4698.         self.isolated_local_models = [Foo]
    
  4699.         with connection.schema_editor() as editor:
    
  4700.             editor.create_model(Foo)
    
  4701. 
    
  4702.         self.assertEqual(
    
  4703.             self.get_column_collation(Foo._meta.db_table, "field"),
    
  4704.             collation,
    
  4705.         )
    
  4706. 
    
  4707.     @isolate_apps("schema")
    
  4708.     @skipUnlessDBFeature("supports_collation_on_textfield")
    
  4709.     def test_db_collation_textfield(self):
    
  4710.         collation = connection.features.test_collations.get("non_default")
    
  4711.         if not collation:
    
  4712.             self.skipTest("Language collations are not supported.")
    
  4713. 
    
  4714.         class Foo(Model):
    
  4715.             field = TextField(db_collation=collation)
    
  4716. 
    
  4717.             class Meta:
    
  4718.                 app_label = "schema"
    
  4719. 
    
  4720.         self.isolated_local_models = [Foo]
    
  4721.         with connection.schema_editor() as editor:
    
  4722.             editor.create_model(Foo)
    
  4723. 
    
  4724.         self.assertEqual(
    
  4725.             self.get_column_collation(Foo._meta.db_table, "field"),
    
  4726.             collation,
    
  4727.         )
    
  4728. 
    
  4729.     @skipUnlessDBFeature("supports_collation_on_charfield")
    
  4730.     def test_add_field_db_collation(self):
    
  4731.         collation = connection.features.test_collations.get("non_default")
    
  4732.         if not collation:
    
  4733.             self.skipTest("Language collations are not supported.")
    
  4734. 
    
  4735.         with connection.schema_editor() as editor:
    
  4736.             editor.create_model(Author)
    
  4737. 
    
  4738.         new_field = CharField(max_length=255, db_collation=collation)
    
  4739.         new_field.set_attributes_from_name("alias")
    
  4740.         with connection.schema_editor() as editor:
    
  4741.             editor.add_field(Author, new_field)
    
  4742.         columns = self.column_classes(Author)
    
  4743.         self.assertEqual(
    
  4744.             columns["alias"][0],
    
  4745.             connection.features.introspected_field_types["CharField"],
    
  4746.         )
    
  4747.         self.assertEqual(columns["alias"][1][8], collation)
    
  4748. 
    
  4749.     @skipUnlessDBFeature("supports_collation_on_charfield")
    
  4750.     def test_alter_field_db_collation(self):
    
  4751.         collation = connection.features.test_collations.get("non_default")
    
  4752.         if not collation:
    
  4753.             self.skipTest("Language collations are not supported.")
    
  4754. 
    
  4755.         with connection.schema_editor() as editor:
    
  4756.             editor.create_model(Author)
    
  4757. 
    
  4758.         old_field = Author._meta.get_field("name")
    
  4759.         new_field = CharField(max_length=255, db_collation=collation)
    
  4760.         new_field.set_attributes_from_name("name")
    
  4761.         new_field.model = Author
    
  4762.         with connection.schema_editor() as editor:
    
  4763.             editor.alter_field(Author, old_field, new_field, strict=True)
    
  4764.         self.assertEqual(
    
  4765.             self.get_column_collation(Author._meta.db_table, "name"),
    
  4766.             collation,
    
  4767.         )
    
  4768.         with connection.schema_editor() as editor:
    
  4769.             editor.alter_field(Author, new_field, old_field, strict=True)
    
  4770.         self.assertIsNone(self.get_column_collation(Author._meta.db_table, "name"))
    
  4771. 
    
  4772.     @skipUnlessDBFeature("supports_collation_on_charfield")
    
  4773.     def test_alter_primary_key_db_collation(self):
    
  4774.         collation = connection.features.test_collations.get("non_default")
    
  4775.         if not collation:
    
  4776.             self.skipTest("Language collations are not supported.")
    
  4777. 
    
  4778.         with connection.schema_editor() as editor:
    
  4779.             editor.create_model(Thing)
    
  4780. 
    
  4781.         old_field = Thing._meta.get_field("when")
    
  4782.         new_field = CharField(max_length=1, db_collation=collation, primary_key=True)
    
  4783.         new_field.set_attributes_from_name("when")
    
  4784.         new_field.model = Thing
    
  4785.         with connection.schema_editor() as editor:
    
  4786.             editor.alter_field(Thing, old_field, new_field, strict=True)
    
  4787.         self.assertEqual(self.get_primary_key(Thing._meta.db_table), "when")
    
  4788.         self.assertEqual(
    
  4789.             self.get_column_collation(Thing._meta.db_table, "when"),
    
  4790.             collation,
    
  4791.         )
    
  4792.         with connection.schema_editor() as editor:
    
  4793.             editor.alter_field(Thing, new_field, old_field, strict=True)
    
  4794.         self.assertEqual(self.get_primary_key(Thing._meta.db_table), "when")
    
  4795.         self.assertIsNone(self.get_column_collation(Thing._meta.db_table, "when"))
    
  4796. 
    
  4797.     @skipUnlessDBFeature(
    
  4798.         "supports_collation_on_charfield", "supports_collation_on_textfield"
    
  4799.     )
    
  4800.     def test_alter_field_type_and_db_collation(self):
    
  4801.         collation = connection.features.test_collations.get("non_default")
    
  4802.         if not collation:
    
  4803.             self.skipTest("Language collations are not supported.")
    
  4804. 
    
  4805.         with connection.schema_editor() as editor:
    
  4806.             editor.create_model(Note)
    
  4807. 
    
  4808.         old_field = Note._meta.get_field("info")
    
  4809.         new_field = CharField(max_length=255, db_collation=collation)
    
  4810.         new_field.set_attributes_from_name("info")
    
  4811.         new_field.model = Note
    
  4812.         with connection.schema_editor() as editor:
    
  4813.             editor.alter_field(Note, old_field, new_field, strict=True)
    
  4814.         columns = self.column_classes(Note)
    
  4815.         self.assertEqual(
    
  4816.             columns["info"][0],
    
  4817.             connection.features.introspected_field_types["CharField"],
    
  4818.         )
    
  4819.         self.assertEqual(columns["info"][1][8], collation)
    
  4820.         with connection.schema_editor() as editor:
    
  4821.             editor.alter_field(Note, new_field, old_field, strict=True)
    
  4822.         columns = self.column_classes(Note)
    
  4823.         self.assertEqual(columns["info"][0], "TextField")
    
  4824.         self.assertIsNone(columns["info"][1][8])
    
  4825. 
    
  4826.     @skipUnlessDBFeature(
    
  4827.         "supports_collation_on_charfield",
    
  4828.         "supports_non_deterministic_collations",
    
  4829.     )
    
  4830.     def test_ci_cs_db_collation(self):
    
  4831.         cs_collation = connection.features.test_collations.get("cs")
    
  4832.         ci_collation = connection.features.test_collations.get("ci")
    
  4833.         try:
    
  4834.             if connection.vendor == "mysql":
    
  4835.                 cs_collation = "latin1_general_cs"
    
  4836.             elif connection.vendor == "postgresql":
    
  4837.                 cs_collation = "en-x-icu"
    
  4838.                 with connection.cursor() as cursor:
    
  4839.                     cursor.execute(
    
  4840.                         "CREATE COLLATION IF NOT EXISTS case_insensitive "
    
  4841.                         "(provider = icu, locale = 'und-u-ks-level2', "
    
  4842.                         "deterministic = false)"
    
  4843.                     )
    
  4844.                     ci_collation = "case_insensitive"
    
  4845.             # Create the table.
    
  4846.             with connection.schema_editor() as editor:
    
  4847.                 editor.create_model(Author)
    
  4848.             # Case-insensitive collation.
    
  4849.             old_field = Author._meta.get_field("name")
    
  4850.             new_field_ci = CharField(max_length=255, db_collation=ci_collation)
    
  4851.             new_field_ci.set_attributes_from_name("name")
    
  4852.             new_field_ci.model = Author
    
  4853.             with connection.schema_editor() as editor:
    
  4854.                 editor.alter_field(Author, old_field, new_field_ci, strict=True)
    
  4855.             Author.objects.create(name="ANDREW")
    
  4856.             self.assertIs(Author.objects.filter(name="Andrew").exists(), True)
    
  4857.             # Case-sensitive collation.
    
  4858.             new_field_cs = CharField(max_length=255, db_collation=cs_collation)
    
  4859.             new_field_cs.set_attributes_from_name("name")
    
  4860.             new_field_cs.model = Author
    
  4861.             with connection.schema_editor() as editor:
    
  4862.                 editor.alter_field(Author, new_field_ci, new_field_cs, strict=True)
    
  4863.             self.assertIs(Author.objects.filter(name="Andrew").exists(), False)
    
  4864.         finally:
    
  4865.             if connection.vendor == "postgresql":
    
  4866.                 with connection.cursor() as cursor:
    
  4867.                     cursor.execute("DROP COLLATION IF EXISTS case_insensitive")