diff options
Diffstat (limited to 'tests/schema')
| -rw-r--r-- | tests/schema/fields.py | 63 | ||||
| -rw-r--r-- | tests/schema/models.py | 8 | ||||
| -rw-r--r-- | tests/schema/test_logging.py | 7 | ||||
| -rw-r--r-- | tests/schema/tests.py | 2190 |
4 files changed, 1344 insertions, 924 deletions
diff --git a/tests/schema/fields.py b/tests/schema/fields.py index 0e567e2d19..998cb28b8c 100644 --- a/tests/schema/fields.py +++ b/tests/schema/fields.py @@ -2,7 +2,9 @@ from functools import partial from django.db import models from django.db.models.fields.related import ( - RECURSIVE_RELATIONSHIP_CONSTANT, ManyToManyDescriptor, RelatedField, + RECURSIVE_RELATIONSHIP_CONSTANT, + ManyToManyDescriptor, + RelatedField, create_many_to_many_intermediary_model, ) @@ -12,29 +14,45 @@ class CustomManyToManyField(RelatedField): Ticket #24104 - Need to have a custom ManyToManyField, which is not an inheritor of ManyToManyField. """ + many_to_many = True - def __init__(self, to, db_constraint=True, swappable=True, related_name=None, related_query_name=None, - limit_choices_to=None, symmetrical=None, through=None, through_fields=None, db_table=None, **kwargs): + def __init__( + self, + to, + db_constraint=True, + swappable=True, + related_name=None, + related_query_name=None, + limit_choices_to=None, + symmetrical=None, + through=None, + through_fields=None, + db_table=None, + **kwargs, + ): try: to._meta except AttributeError: to = str(to) - kwargs['rel'] = models.ManyToManyRel( - self, to, + kwargs["rel"] = models.ManyToManyRel( + self, + to, related_name=related_name, related_query_name=related_query_name, limit_choices_to=limit_choices_to, - symmetrical=symmetrical if symmetrical is not None else (to == RECURSIVE_RELATIONSHIP_CONSTANT), + symmetrical=symmetrical + if symmetrical is not None + else (to == RECURSIVE_RELATIONSHIP_CONSTANT), through=through, through_fields=through_fields, db_constraint=db_constraint, ) self.swappable = swappable self.db_table = db_table - if kwargs['rel'].through is not None and self.db_table is not None: + if kwargs["rel"].through is not None and self.db_table is not None: raise ValueError( - 'Cannot specify a db_table if an intermediary model is used.' + "Cannot specify a db_table if an intermediary model is used." ) super().__init__( related_name=related_name, @@ -45,22 +63,32 @@ class CustomManyToManyField(RelatedField): def contribute_to_class(self, cls, name, **kwargs): if self.remote_field.symmetrical and ( - self.remote_field.model == "self" or self.remote_field.model == cls._meta.object_name): + self.remote_field.model == "self" + or self.remote_field.model == cls._meta.object_name + ): self.remote_field.related_name = "%s_rel_+" % name super().contribute_to_class(cls, name, **kwargs) - if not self.remote_field.through and not cls._meta.abstract and not cls._meta.swapped: - self.remote_field.through = create_many_to_many_intermediary_model(self, cls) + if ( + not self.remote_field.through + and not cls._meta.abstract + and not cls._meta.swapped + ): + self.remote_field.through = create_many_to_many_intermediary_model( + self, cls + ) setattr(cls, self.name, ManyToManyDescriptor(self.remote_field)) self.m2m_db_table = partial(self._get_m2m_db_table, cls._meta) def get_internal_type(self): - return 'ManyToManyField' + return "ManyToManyField" # Copy those methods from ManyToManyField because they don't call super() internally - contribute_to_related_class = models.ManyToManyField.__dict__['contribute_to_related_class'] - _get_m2m_attr = models.ManyToManyField.__dict__['_get_m2m_attr'] - _get_m2m_reverse_attr = models.ManyToManyField.__dict__['_get_m2m_reverse_attr'] - _get_m2m_db_table = models.ManyToManyField.__dict__['_get_m2m_db_table'] + contribute_to_related_class = models.ManyToManyField.__dict__[ + "contribute_to_related_class" + ] + _get_m2m_attr = models.ManyToManyField.__dict__["_get_m2m_attr"] + _get_m2m_reverse_attr = models.ManyToManyField.__dict__["_get_m2m_reverse_attr"] + _get_m2m_db_table = models.ManyToManyField.__dict__["_get_m2m_db_table"] class InheritedManyToManyField(models.ManyToManyField): @@ -71,5 +99,6 @@ class MediumBlobField(models.BinaryField): """ A MySQL BinaryField that uses a different blob size. """ + def db_type(self, connection): - return 'MEDIUMBLOB' + return "MEDIUMBLOB" diff --git a/tests/schema/models.py b/tests/schema/models.py index 75e4de0874..55f7d2c13c 100644 --- a/tests/schema/models.py +++ b/tests/schema/models.py @@ -68,7 +68,7 @@ class AuthorWithIndexedNameAndBirthday(models.Model): class Meta: apps = new_apps - index_together = [['name', 'birthday']] + index_together = [["name", "birthday"]] class AuthorWithUniqueNameAndBirthday(models.Model): @@ -77,7 +77,7 @@ class AuthorWithUniqueNameAndBirthday(models.Model): class Meta: apps = new_apps - unique_together = [['name', 'birthday']] + unique_together = [["name", "birthday"]] class Book(models.Model): @@ -212,7 +212,7 @@ class Thing(models.Model): class Meta: apps = new_apps - db_table = 'drop' + db_table = "drop" def __str__(self): return self.when @@ -229,7 +229,7 @@ class UniqueTest(models.Model): class Node(models.Model): node_id = models.AutoField(primary_key=True) - parent = models.ForeignKey('self', models.CASCADE, null=True, blank=True) + parent = models.ForeignKey("self", models.CASCADE, null=True, blank=True) class Meta: apps = new_apps diff --git a/tests/schema/test_logging.py b/tests/schema/test_logging.py index 453bdd798e..2821e5f406 100644 --- a/tests/schema/test_logging.py +++ b/tests/schema/test_logging.py @@ -3,16 +3,15 @@ from django.test import TestCase class SchemaLoggerTests(TestCase): - def test_extra_args(self): editor = connection.schema_editor(collect_sql=True) - sql = 'SELECT * FROM foo WHERE id in (%s, %s)' + sql = "SELECT * FROM foo WHERE id in (%s, %s)" params = [42, 1337] - with self.assertLogs('django.db.backends.schema', 'DEBUG') as cm: + with self.assertLogs("django.db.backends.schema", "DEBUG") as cm: editor.execute(sql, params) self.assertEqual(cm.records[0].sql, sql) self.assertEqual(cm.records[0].params, params) self.assertEqual( cm.records[0].getMessage(), - 'SELECT * FROM foo WHERE id in (%s, %s); (params [42, 1337])', + "SELECT * FROM foo WHERE id in (%s, %s); (params [42, 1337])", ) diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 60e69503cc..267a7c971b 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -7,40 +7,85 @@ from unittest import mock from django.core.exceptions import FieldError from django.core.management.color import no_style from django.db import ( - DatabaseError, DataError, IntegrityError, OperationalError, connection, + DatabaseError, + DataError, + IntegrityError, + OperationalError, + connection, ) from django.db.models import ( - CASCADE, PROTECT, AutoField, BigAutoField, BigIntegerField, BinaryField, - BooleanField, CharField, CheckConstraint, DateField, DateTimeField, - DecimalField, DurationField, F, FloatField, ForeignKey, ForeignObject, - Index, IntegerField, JSONField, ManyToManyField, Model, OneToOneField, - OrderBy, PositiveIntegerField, Q, SlugField, SmallAutoField, - SmallIntegerField, TextField, TimeField, UniqueConstraint, UUIDField, + CASCADE, + PROTECT, + AutoField, + BigAutoField, + BigIntegerField, + BinaryField, + BooleanField, + CharField, + CheckConstraint, + DateField, + DateTimeField, + DecimalField, + DurationField, + F, + FloatField, + ForeignKey, + ForeignObject, + Index, + IntegerField, + JSONField, + ManyToManyField, + Model, + OneToOneField, + OrderBy, + PositiveIntegerField, + Q, + SlugField, + SmallAutoField, + SmallIntegerField, + TextField, + TimeField, + UniqueConstraint, + UUIDField, Value, ) from django.db.models.fields.json import KeyTextTransform from django.db.models.functions import Abs, Cast, Collate, Lower, Random, Upper from django.db.models.indexes import IndexExpression from django.db.transaction import TransactionManagementError, atomic -from django.test import ( - TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, -) -from django.test.utils import ( - CaptureQueriesContext, isolate_apps, register_lookup, -) +from django.test import TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature +from django.test.utils import CaptureQueriesContext, isolate_apps, register_lookup from django.utils import timezone -from .fields import ( - CustomManyToManyField, InheritedManyToManyField, MediumBlobField, -) +from .fields import CustomManyToManyField, InheritedManyToManyField, MediumBlobField from .models import ( - Author, AuthorCharFieldWithIndex, AuthorTextFieldWithIndex, - AuthorWithDefaultHeight, AuthorWithEvenLongerName, AuthorWithIndexedName, - AuthorWithIndexedNameAndBirthday, AuthorWithUniqueName, - AuthorWithUniqueNameAndBirthday, Book, BookForeignObj, BookWeak, - BookWithLongName, BookWithO2O, BookWithoutAuthor, BookWithSlug, IntegerPK, - Node, Note, NoteRename, Tag, TagIndexed, TagM2MTest, TagUniqueRename, - Thing, UniqueTest, new_apps, + Author, + AuthorCharFieldWithIndex, + AuthorTextFieldWithIndex, + AuthorWithDefaultHeight, + AuthorWithEvenLongerName, + AuthorWithIndexedName, + AuthorWithIndexedNameAndBirthday, + AuthorWithUniqueName, + AuthorWithUniqueNameAndBirthday, + Book, + BookForeignObj, + BookWeak, + BookWithLongName, + BookWithO2O, + BookWithoutAuthor, + BookWithSlug, + IntegerPK, + Node, + Note, + NoteRename, + Tag, + TagIndexed, + TagM2MTest, + TagUniqueRename, + Thing, + UniqueTest, + new_apps, ) @@ -56,10 +101,25 @@ class SchemaTests(TransactionTestCase): available_apps = [] models = [ - Author, AuthorCharFieldWithIndex, AuthorTextFieldWithIndex, - AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book, BookWeak, - BookWithLongName, BookWithO2O, BookWithSlug, IntegerPK, Node, Note, - Tag, TagIndexed, TagM2MTest, TagUniqueRename, Thing, UniqueTest, + Author, + AuthorCharFieldWithIndex, + AuthorTextFieldWithIndex, + AuthorWithDefaultHeight, + AuthorWithEvenLongerName, + Book, + BookWeak, + BookWithLongName, + BookWithO2O, + BookWithSlug, + IntegerPK, + Node, + Note, + Tag, + TagIndexed, + TagM2MTest, + TagUniqueRename, + Thing, + UniqueTest, ] # Utility functions @@ -78,13 +138,13 @@ class SchemaTests(TransactionTestCase): new_apps.clear_cache() for model in new_apps.get_models(): model._meta._expire_cache() - if 'schema' in new_apps.all_models: + if "schema" in new_apps.all_models: for model in self.local_models: for many_to_many in model._meta.many_to_many: through = many_to_many.remote_field.through if through and through._meta.auto_created: - del new_apps.all_models['schema'][through._meta.model_name] - del new_apps.all_models['schema'][model._meta.model_name] + del new_apps.all_models["schema"][through._meta.model_name] + del new_apps.all_models["schema"][model._meta.model_name] if self.isolated_local_models: with connection.schema_editor() as editor: for model in self.isolated_local_models: @@ -132,17 +192,21 @@ class SchemaTests(TransactionTestCase): """ with connection.cursor() as cursor: return [ - c['columns'][0] - for c in connection.introspection.get_constraints(cursor, table).values() - if c['index'] and len(c['columns']) == 1 + c["columns"][0] + for c in connection.introspection.get_constraints( + cursor, table + ).values() + if c["index"] and len(c["columns"]) == 1 ] def get_uniques(self, table): with connection.cursor() as cursor: return [ - c['columns'][0] - for c in connection.introspection.get_constraints(cursor, table).values() - if c['unique'] and len(c['columns']) == 1 + c["columns"][0] + for c in connection.introspection.get_constraints( + cursor, table + ).values() + if c["unique"] and len(c["columns"]) == 1 ] def get_constraints(self, table): @@ -156,15 +220,24 @@ class SchemaTests(TransactionTestCase): constraints = self.get_constraints(model._meta.db_table) constraints_for_column = [] for name, details in constraints.items(): - if details['columns'] == [column_name]: + if details["columns"] == [column_name]: constraints_for_column.append(name) return sorted(constraints_for_column) - def check_added_field_default(self, schema_editor, model, field, field_name, expected_default, - cast_function=None): + def check_added_field_default( + self, + schema_editor, + model, + field, + field_name, + expected_default, + cast_function=None, + ): with connection.cursor() as cursor: schema_editor.add_field(model, field) - cursor.execute("SELECT {} FROM {};".format(field_name, model._meta.db_table)) + cursor.execute( + "SELECT {} FROM {};".format(field_name, model._meta.db_table) + ) database_default = cursor.fetchall()[0][0] if cast_function and type(database_default) != type(expected_default): database_default = cast_function(database_default) @@ -179,15 +252,15 @@ class SchemaTests(TransactionTestCase): """ with connection.cursor() as cursor: constraints = connection.introspection.get_constraints(cursor, table) - counts = {'fks': 0, 'uniques': 0, 'indexes': 0} + counts = {"fks": 0, "uniques": 0, "indexes": 0} for c in constraints.values(): - if c['columns'] == [column]: - if c['foreign_key'] == fk_to: - counts['fks'] += 1 - if c['unique']: - counts['uniques'] += 1 - elif c['index']: - counts['indexes'] += 1 + if c["columns"] == [column]: + if c["foreign_key"] == fk_to: + counts["fks"] += 1 + if c["unique"]: + counts["uniques"] += 1 + elif c["index"]: + counts["indexes"] += 1 return counts def get_column_collation(self, table, column): @@ -201,10 +274,12 @@ class SchemaTests(TransactionTestCase): def assertIndexOrder(self, table, index, order): constraints = self.get_constraints(table) self.assertIn(index, constraints) - index_orders = constraints[index]['orders'] - self.assertTrue(all(val == expected for val, expected in zip(index_orders, order))) + index_orders = constraints[index]["orders"] + self.assertTrue( + all(val == expected for val, expected in zip(index_orders, order)) + ) - def assertForeignKeyExists(self, model, column, expected_fk_table, field='id'): + def assertForeignKeyExists(self, model, column, expected_fk_table, field="id"): """ Fail if the FK constraint on `model.Meta.db_table`.`column` to `expected_fk_table`.id doesn't exist. @@ -212,8 +287,8 @@ class SchemaTests(TransactionTestCase): constraints = self.get_constraints(model._meta.db_table) constraint_fk = None for details in constraints.values(): - if details['columns'] == [column] and details['foreign_key']: - constraint_fk = details['foreign_key'] + if details["columns"] == [column] and details["foreign_key"]: + constraint_fk = details["foreign_key"] break self.assertEqual(constraint_fk, (expected_fk_table, field)) @@ -239,7 +314,7 @@ class SchemaTests(TransactionTestCase): with self.assertRaises(DatabaseError): list(Author.objects.all()) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_fk(self): "Creating tables out of FK order, then repointing, works" # Create the table @@ -263,124 +338,138 @@ class SchemaTests(TransactionTestCase): new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(Book, old_field, new_field, strict=True) - self.assertForeignKeyExists(Book, 'author_id', 'schema_tag') + self.assertForeignKeyExists(Book, "author_id", "schema_tag") - @skipUnlessDBFeature('can_create_inline_fk') + @skipUnlessDBFeature("can_create_inline_fk") def test_inline_fk(self): # Create some tables. with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) editor.create_model(Note) - self.assertForeignKeyNotExists(Note, 'book_id', 'schema_book') + self.assertForeignKeyNotExists(Note, "book_id", "schema_book") # Add a foreign key from one to the other. with connection.schema_editor() as editor: new_field = ForeignKey(Book, CASCADE) - new_field.set_attributes_from_name('book') + new_field.set_attributes_from_name("book") editor.add_field(Note, new_field) - self.assertForeignKeyExists(Note, 'book_id', 'schema_book') + self.assertForeignKeyExists(Note, "book_id", "schema_book") # Creating a FK field with a constraint uses a single statement without # a deferred ALTER TABLE. - self.assertFalse([ - sql for sql in (str(statement) for statement in editor.deferred_sql) - if sql.startswith('ALTER TABLE') and 'ADD CONSTRAINT' in sql - ]) + self.assertFalse( + [ + sql + for sql in (str(statement) for statement in editor.deferred_sql) + if sql.startswith("ALTER TABLE") and "ADD CONSTRAINT" in sql + ] + ) - @skipUnlessDBFeature('can_create_inline_fk') + @skipUnlessDBFeature("can_create_inline_fk") def test_add_inline_fk_update_data(self): with connection.schema_editor() as editor: editor.create_model(Node) # Add an inline foreign key and update data in the same transaction. - new_field = ForeignKey(Node, CASCADE, related_name='new_fk', null=True) - new_field.set_attributes_from_name('new_parent_fk') + new_field = ForeignKey(Node, CASCADE, related_name="new_fk", null=True) + new_field.set_attributes_from_name("new_parent_fk") parent = Node.objects.create() with connection.schema_editor() as editor: editor.add_field(Node, new_field) - editor.execute('UPDATE schema_node SET new_parent_fk_id = %s;', [parent.pk]) + editor.execute("UPDATE schema_node SET new_parent_fk_id = %s;", [parent.pk]) assertIndex = ( self.assertIn if connection.features.indexes_foreign_keys else self.assertNotIn ) - assertIndex('new_parent_fk_id', self.get_indexes(Node._meta.db_table)) + assertIndex("new_parent_fk_id", self.get_indexes(Node._meta.db_table)) @skipUnlessDBFeature( - 'can_create_inline_fk', - 'allows_multiple_constraints_on_same_fields', + "can_create_inline_fk", + "allows_multiple_constraints_on_same_fields", ) - @isolate_apps('schema') + @isolate_apps("schema") def test_add_inline_fk_index_update_data(self): class Node(Model): class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Node) # Add an inline foreign key, update data, and an index in the same # transaction. - new_field = ForeignKey(Node, CASCADE, related_name='new_fk', null=True) - new_field.set_attributes_from_name('new_parent_fk') + new_field = ForeignKey(Node, CASCADE, related_name="new_fk", null=True) + new_field.set_attributes_from_name("new_parent_fk") parent = Node.objects.create() with connection.schema_editor() as editor: editor.add_field(Node, new_field) Node._meta.add_field(new_field) - editor.execute('UPDATE schema_node SET new_parent_fk_id = %s;', [parent.pk]) - editor.add_index(Node, Index(fields=['new_parent_fk'], name='new_parent_inline_fk_idx')) - self.assertIn('new_parent_fk_id', self.get_indexes(Node._meta.db_table)) + editor.execute("UPDATE schema_node SET new_parent_fk_id = %s;", [parent.pk]) + editor.add_index( + Node, Index(fields=["new_parent_fk"], name="new_parent_inline_fk_idx") + ) + self.assertIn("new_parent_fk_id", self.get_indexes(Node._meta.db_table)) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_char_field_with_db_index_to_fk(self): # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(AuthorCharFieldWithIndex) # Change CharField to FK - old_field = AuthorCharFieldWithIndex._meta.get_field('char_field') + old_field = AuthorCharFieldWithIndex._meta.get_field("char_field") new_field = ForeignKey(Author, CASCADE, blank=True) - new_field.set_attributes_from_name('char_field') + new_field.set_attributes_from_name("char_field") with connection.schema_editor() as editor: - editor.alter_field(AuthorCharFieldWithIndex, old_field, new_field, strict=True) - self.assertForeignKeyExists(AuthorCharFieldWithIndex, 'char_field_id', 'schema_author') + editor.alter_field( + AuthorCharFieldWithIndex, old_field, new_field, strict=True + ) + self.assertForeignKeyExists( + AuthorCharFieldWithIndex, "char_field_id", "schema_author" + ) - @skipUnlessDBFeature('supports_foreign_keys') - @skipUnlessDBFeature('supports_index_on_text_field') + @skipUnlessDBFeature("supports_foreign_keys") + @skipUnlessDBFeature("supports_index_on_text_field") def test_text_field_with_db_index_to_fk(self): # Create the table with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(AuthorTextFieldWithIndex) # Change TextField to FK - old_field = AuthorTextFieldWithIndex._meta.get_field('text_field') + old_field = AuthorTextFieldWithIndex._meta.get_field("text_field") new_field = ForeignKey(Author, CASCADE, blank=True) - new_field.set_attributes_from_name('text_field') + new_field.set_attributes_from_name("text_field") with connection.schema_editor() as editor: - editor.alter_field(AuthorTextFieldWithIndex, old_field, new_field, strict=True) - self.assertForeignKeyExists(AuthorTextFieldWithIndex, 'text_field_id', 'schema_author') + editor.alter_field( + AuthorTextFieldWithIndex, old_field, new_field, strict=True + ) + self.assertForeignKeyExists( + AuthorTextFieldWithIndex, "text_field_id", "schema_author" + ) - @isolate_apps('schema') + @isolate_apps("schema") def test_char_field_pk_to_auto_field(self): class Foo(Model): id = CharField(max_length=255, primary_key=True) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Foo) self.isolated_local_models = [Foo] - old_field = Foo._meta.get_field('id') + old_field = Foo._meta.get_field("id") new_field = AutoField(primary_key=True) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") new_field.model = Foo with connection.schema_editor() as editor: editor.alter_field(Foo, old_field, new_field, strict=True) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_fk_to_proxy(self): "Creating a FK to a proxy model creates database constraints." + class AuthorProxy(Author): class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps proxy = True @@ -388,7 +477,7 @@ class SchemaTests(TransactionTestCase): author = ForeignKey(AuthorProxy, on_delete=CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [AuthorProxy, AuthorRef] @@ -397,9 +486,9 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(AuthorRef) - self.assertForeignKeyExists(AuthorRef, 'author_id', 'schema_author') + self.assertForeignKeyExists(AuthorRef, "author_id", "schema_author") - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_fk_db_constraint(self): "The db_constraint parameter is respected" # Create the table @@ -411,63 +500,64 @@ class SchemaTests(TransactionTestCase): list(Author.objects.all()) list(Tag.objects.all()) list(BookWeak.objects.all()) - self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author') + self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author") # Make a db_constraint=False FK new_field = ForeignKey(Tag, CASCADE, db_constraint=False) new_field.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.add_field(Author, new_field) - self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag') + self.assertForeignKeyNotExists(Author, "tag_id", "schema_tag") # Alter to one with a constraint new_field2 = ForeignKey(Tag, CASCADE) new_field2.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.alter_field(Author, new_field, new_field2, strict=True) - self.assertForeignKeyExists(Author, 'tag_id', 'schema_tag') + self.assertForeignKeyExists(Author, "tag_id", "schema_tag") # Alter to one without a constraint again new_field2 = ForeignKey(Tag, CASCADE) new_field2.set_attributes_from_name("tag") with connection.schema_editor() as editor: editor.alter_field(Author, new_field2, new_field, strict=True) - self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag') + self.assertForeignKeyNotExists(Author, "tag_id", "schema_tag") - @isolate_apps('schema') + @isolate_apps("schema") def test_no_db_constraint_added_during_primary_key_change(self): """ When a primary key that's pointed to by a ForeignKey with db_constraint=False is altered, a foreign key constraint isn't added. """ + class Author(Model): class Meta: - app_label = 'schema' + app_label = "schema" class BookWeak(Model): author = ForeignKey(Author, CASCADE, db_constraint=False) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(BookWeak) - self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author') - old_field = Author._meta.get_field('id') + self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author") + old_field = Author._meta.get_field("id") new_field = BigAutoField(primary_key=True) new_field.model = Author - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") # @isolate_apps() and inner models are needed to have the model # relations populated, otherwise this doesn't act as a regression test. self.assertEqual(len(new_field.model._meta.related_objects), 1) with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author') + self.assertForeignKeyNotExists(BookWeak, "author_id", "schema_author") def _test_m2m_db_constraint(self, M2MFieldClass): class LocalAuthorWithM2M(Model): name = CharField(max_length=255) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [LocalAuthorWithM2M] @@ -485,17 +575,19 @@ class SchemaTests(TransactionTestCase): # Add the field with connection.schema_editor() as editor: editor.add_field(LocalAuthorWithM2M, new_field) - self.assertForeignKeyNotExists(new_field.remote_field.through, 'tag_id', 'schema_tag') + self.assertForeignKeyNotExists( + new_field.remote_field.through, "tag_id", "schema_tag" + ) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_m2m_db_constraint(self): self._test_m2m_db_constraint(ManyToManyField) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_m2m_db_constraint_custom(self): self._test_m2m_db_constraint(CustomManyToManyField) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_m2m_db_constraint_inherited(self): self._test_m2m_db_constraint(InheritedManyToManyField) @@ -512,24 +604,29 @@ class SchemaTests(TransactionTestCase): # Add the new field new_field = IntegerField(null=True) new_field.set_attributes_from_name("age") - with CaptureQueriesContext(connection) as ctx, connection.schema_editor() as editor: + with CaptureQueriesContext( + connection + ) as ctx, connection.schema_editor() as editor: editor.add_field(Author, new_field) drop_default_sql = editor.sql_alter_column_no_default % { - 'column': editor.quote_name(new_field.name), + "column": editor.quote_name(new_field.name), } - self.assertFalse(any(drop_default_sql in query['sql'] for query in ctx.captured_queries)) + self.assertFalse( + any(drop_default_sql in query["sql"] for query in ctx.captured_queries) + ) # Table is not rebuilt. - self.assertIs(any( - 'CREATE TABLE' in query['sql'] - for query in ctx.captured_queries - ), False) - self.assertIs(any( - 'DROP TABLE' in query['sql'] - for query in ctx.captured_queries - ), False) + self.assertIs( + any("CREATE TABLE" in query["sql"] for query in ctx.captured_queries), False + ) + self.assertIs( + any("DROP TABLE" in query["sql"] for query in ctx.captured_queries), False + ) columns = self.column_classes(Author) - self.assertEqual(columns['age'][0], connection.features.introspected_field_types['IntegerField']) - self.assertTrue(columns['age'][1][6]) + self.assertEqual( + columns["age"][0], + connection.features.introspected_field_types["IntegerField"], + ) + self.assertTrue(columns["age"][1][6]) def test_add_field_remove_field(self): """ @@ -539,7 +636,7 @@ class SchemaTests(TransactionTestCase): # Create a table with a unique constraint on the slug field. editor.create_model(Tag) # Remove the slug column. - editor.remove_field(Tag, Tag._meta.get_field('slug')) + editor.remove_field(Tag, Tag._meta.get_field("slug")) self.assertEqual(editor.deferred_sql, []) def test_add_field_temp_default(self): @@ -561,9 +658,14 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.add_field(Author, new_field) columns = self.column_classes(Author) - self.assertEqual(columns['surname'][0], connection.features.introspected_field_types['CharField']) - self.assertEqual(columns['surname'][1][6], - connection.features.interprets_empty_strings_as_nulls) + self.assertEqual( + columns["surname"][0], + connection.features.introspected_field_types["CharField"], + ) + self.assertEqual( + columns["surname"][1][6], + connection.features.interprets_empty_strings_as_nulls, + ) def test_add_field_temp_default_boolean(self): """ @@ -586,8 +688,10 @@ class SchemaTests(TransactionTestCase): editor.add_field(Author, new_field) columns = self.column_classes(Author) # BooleanField are stored as TINYINT(1) on MySQL. - field_type = columns['awesome'][0] - self.assertEqual(field_type, connection.features.introspected_field_types['BooleanField']) + field_type = columns["awesome"][0] + self.assertEqual( + field_type, connection.features.introspected_field_types["BooleanField"] + ) def test_add_field_default_transform(self): """ @@ -619,8 +723,10 @@ class SchemaTests(TransactionTestCase): editor.add_field(Author, new_field) # Ensure the field is there columns = self.column_classes(Author) - field_type, field_info = columns['thing'] - self.assertEqual(field_type, connection.features.introspected_field_types['IntegerField']) + field_type, field_info = columns["thing"] + self.assertEqual( + field_type, connection.features.introspected_field_types["IntegerField"] + ) # Make sure the values were transformed correctly self.assertEqual(Author.objects.extra(where=["thing = 1"]).count(), 2) @@ -629,12 +735,12 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) editor.create_model(Note) new_field = OneToOneField(Note, CASCADE, null=True) - new_field.set_attributes_from_name('note') + new_field.set_attributes_from_name("note") with connection.schema_editor() as editor: editor.add_field(Author, new_field) columns = self.column_classes(Author) - self.assertIn('note_id', columns) - self.assertTrue(columns['note_id'][1][6]) + self.assertIn("note_id", columns) + self.assertTrue(columns["note_id"][1][6]) def test_add_field_binary(self): """ @@ -651,22 +757,22 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(Author) # MySQL annoyingly uses the same backend, so it'll come back as one of # these two types. - self.assertIn(columns['bits'][0], ("BinaryField", "TextField")) + self.assertIn(columns["bits"][0], ("BinaryField", "TextField")) def test_add_field_durationfield_with_default(self): with connection.schema_editor() as editor: editor.create_model(Author) new_field = DurationField(default=datetime.timedelta(minutes=10)) - new_field.set_attributes_from_name('duration') + new_field.set_attributes_from_name("duration") with connection.schema_editor() as editor: editor.add_field(Author, new_field) columns = self.column_classes(Author) self.assertEqual( - columns['duration'][0], - connection.features.introspected_field_types['DurationField'], + columns["duration"][0], + connection.features.introspected_field_types["DurationField"], ) - @unittest.skipUnless(connection.vendor == 'mysql', "MySQL specific") + @unittest.skipUnless(connection.vendor == "mysql", "MySQL specific") def test_add_binaryfield_mediumblob(self): """ Test adding a custom-sized binary field on MySQL (#24846). @@ -675,13 +781,13 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Add the new field with default - new_field = MediumBlobField(blank=True, default=b'123') - new_field.set_attributes_from_name('bits') + new_field = MediumBlobField(blank=True, default=b"123") + new_field.set_attributes_from_name("bits") with connection.schema_editor() as editor: editor.add_field(Author, new_field) columns = self.column_classes(Author) # Introspection treats BLOBs as TextFields - self.assertEqual(columns['bits'][0], "TextField") + self.assertEqual(columns["bits"][0], "TextField") def test_alter(self): """ @@ -692,8 +798,14 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) # Ensure the field is right to begin with columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField']) - self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls)) + self.assertEqual( + columns["name"][0], + connection.features.introspected_field_types["CharField"], + ) + self.assertEqual( + bool(columns["name"][1][6]), + bool(connection.features.interprets_empty_strings_as_nulls), + ) # Alter the name field to a TextField old_field = Author._meta.get_field("name") new_field = TextField(null=True) @@ -701,25 +813,28 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], "TextField") - self.assertTrue(columns['name'][1][6]) + self.assertEqual(columns["name"][0], "TextField") + self.assertTrue(columns["name"][1][6]) # Change nullability again new_field2 = TextField(null=False) new_field2.set_attributes_from_name("name") with connection.schema_editor() as editor: editor.alter_field(Author, new_field, new_field2, strict=True) columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], "TextField") - self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls)) + self.assertEqual(columns["name"][0], "TextField") + self.assertEqual( + bool(columns["name"][1][6]), + bool(connection.features.interprets_empty_strings_as_nulls), + ) def test_alter_auto_field_to_integer_field(self): # Create the table with connection.schema_editor() as editor: editor.create_model(Author) # Change AutoField to IntegerField - old_field = Author._meta.get_field('id') + old_field = Author._meta.get_field("id") new_field = IntegerField(primary_key=True) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") new_field.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) @@ -734,29 +849,29 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Change AutoField to CharField - old_field = Author._meta.get_field('id') + old_field = Author._meta.get_field("id") new_field = CharField(primary_key=True, max_length=50) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") new_field.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - @isolate_apps('schema') + @isolate_apps("schema") def test_alter_auto_field_quoted_db_column(self): class Foo(Model): id = AutoField(primary_key=True, db_column='"quoted_id"') class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Foo) self.isolated_local_models = [Foo] - old_field = Foo._meta.get_field('id') + old_field = Foo._meta.get_field("id") new_field = BigAutoField(primary_key=True) new_field.model = Foo new_field.db_column = '"quoted_id"' - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") with connection.schema_editor() as editor: editor.alter_field(Foo, old_field, new_field, strict=True) Foo.objects.create() @@ -766,35 +881,35 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Change UUIDField to primary key. - old_field = Author._meta.get_field('uuid') + old_field = Author._meta.get_field("uuid") new_field = UUIDField(primary_key=True) - new_field.set_attributes_from_name('uuid') + new_field.set_attributes_from_name("uuid") new_field.model = Author with connection.schema_editor() as editor: - editor.remove_field(Author, Author._meta.get_field('id')) + editor.remove_field(Author, Author._meta.get_field("id")) editor.alter_field(Author, old_field, new_field, strict=True) # Redundant unique constraint is not added. count = self.get_constraints_count( Author._meta.db_table, - Author._meta.get_field('uuid').column, + Author._meta.get_field("uuid").column, None, ) - self.assertLessEqual(count['uniques'], 1) + self.assertLessEqual(count["uniques"], 1) - @isolate_apps('schema') + @isolate_apps("schema") def test_alter_primary_key_quoted_db_table(self): class Foo(Model): class Meta: - app_label = 'schema' + app_label = "schema" db_table = '"foo"' with connection.schema_editor() as editor: editor.create_model(Foo) self.isolated_local_models = [Foo] - old_field = Foo._meta.get_field('id') + old_field = Foo._meta.get_field("id") new_field = BigAutoField(primary_key=True) new_field.model = Foo - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") with connection.schema_editor() as editor: editor.alter_field(Foo, old_field, new_field, strict=True) Foo.objects.create() @@ -814,13 +929,13 @@ class SchemaTests(TransactionTestCase): def test_alter_text_field_to_not_null_with_default_value(self): with connection.schema_editor() as editor: editor.create_model(Note) - old_field = Note._meta.get_field('address') - new_field = TextField(blank=True, default='', null=False) - new_field.set_attributes_from_name('address') + old_field = Note._meta.get_field("address") + new_field = TextField(blank=True, default="", null=False) + new_field.set_attributes_from_name("address") with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) - @skipUnlessDBFeature('can_defer_constraint_checks', 'can_rollback_ddl') + @skipUnlessDBFeature("can_defer_constraint_checks", "can_rollback_ddl") def test_alter_fk_checks_deferred_constraints(self): """ #25492 - Altering a foreign key's structure and data in the same @@ -828,9 +943,9 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Node) - old_field = Node._meta.get_field('parent') + old_field = Node._meta.get_field("parent") new_field = ForeignKey(Node, CASCADE) - new_field.set_attributes_from_name('parent') + new_field.set_attributes_from_name("parent") parent = Node.objects.create() with connection.schema_editor() as editor: # Update the parent FK to create a deferred constraint check. @@ -843,15 +958,15 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Note) - Note.objects.create(info='1988-05-05') - old_field = Note._meta.get_field('info') + Note.objects.create(info="1988-05-05") + old_field = Note._meta.get_field("info") new_field = DateField(blank=True) - new_field.set_attributes_from_name('info') + new_field.set_attributes_from_name("info") with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) # Make sure the field isn't nullable columns = self.column_classes(Note) - self.assertFalse(columns['info'][1][6]) + self.assertFalse(columns["info"][1][6]) def test_alter_text_field_to_datetime_field(self): """ @@ -859,15 +974,15 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Note) - Note.objects.create(info='1988-05-05 3:16:17.4567') - old_field = Note._meta.get_field('info') + Note.objects.create(info="1988-05-05 3:16:17.4567") + old_field = Note._meta.get_field("info") new_field = DateTimeField(blank=True) - new_field.set_attributes_from_name('info') + new_field.set_attributes_from_name("info") with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) # Make sure the field isn't nullable columns = self.column_classes(Note) - self.assertFalse(columns['info'][1][6]) + self.assertFalse(columns["info"][1][6]) def test_alter_text_field_to_time_field(self): """ @@ -875,17 +990,17 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Note) - Note.objects.create(info='3:16:17.4567') - old_field = Note._meta.get_field('info') + Note.objects.create(info="3:16:17.4567") + old_field = Note._meta.get_field("info") new_field = TimeField(blank=True) - new_field.set_attributes_from_name('info') + new_field.set_attributes_from_name("info") with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) # Make sure the field isn't nullable columns = self.column_classes(Note) - self.assertFalse(columns['info'][1][6]) + self.assertFalse(columns["info"][1][6]) - @skipIfDBFeature('interprets_empty_strings_as_nulls') + @skipIfDBFeature("interprets_empty_strings_as_nulls") def test_alter_textual_field_keep_null_status(self): """ Changing a field type shouldn't affect the not null status. @@ -902,7 +1017,7 @@ class SchemaTests(TransactionTestCase): with self.assertRaises(IntegrityError): Note.objects.create(info=None) - @skipUnlessDBFeature('interprets_empty_strings_as_nulls') + @skipUnlessDBFeature("interprets_empty_strings_as_nulls") def test_alter_textual_field_not_null_to_null(self): """ Nullability for textual fields is preserved on databases that @@ -912,16 +1027,16 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) columns = self.column_classes(Author) # Field is nullable. - self.assertTrue(columns['uuid'][1][6]) + self.assertTrue(columns["uuid"][1][6]) # Change to NOT NULL. - old_field = Author._meta.get_field('uuid') + old_field = Author._meta.get_field("uuid") new_field = SlugField(null=False, blank=True) - new_field.set_attributes_from_name('uuid') + new_field.set_attributes_from_name("uuid") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) columns = self.column_classes(Author) # Nullability is preserved. - self.assertTrue(columns['uuid'][1][6]) + self.assertTrue(columns["uuid"][1][6]) def test_alter_numeric_field_keep_null_status(self): """ @@ -930,14 +1045,14 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(UniqueTest) with self.assertRaises(IntegrityError): - UniqueTest.objects.create(year=None, slug='aaa') + UniqueTest.objects.create(year=None, slug="aaa") old_field = UniqueTest._meta.get_field("year") new_field = BigIntegerField() new_field.set_attributes_from_name("year") with connection.schema_editor() as editor: editor.alter_field(UniqueTest, old_field, new_field, strict=True) with self.assertRaises(IntegrityError): - UniqueTest.objects.create(year=None, slug='bbb') + UniqueTest.objects.create(year=None, slug="bbb") def test_alter_null_to_not_null(self): """ @@ -948,13 +1063,13 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) # Ensure the field is right to begin with columns = self.column_classes(Author) - self.assertTrue(columns['height'][1][6]) + self.assertTrue(columns["height"][1][6]) # Create some test data - Author.objects.create(name='Not null author', height=12) - Author.objects.create(name='Null author') + Author.objects.create(name="Not null author", height=12) + Author.objects.create(name="Null author") # Verify null value - self.assertEqual(Author.objects.get(name='Not null author').height, 12) - self.assertIsNone(Author.objects.get(name='Null author').height) + self.assertEqual(Author.objects.get(name="Not null author").height, 12) + self.assertIsNone(Author.objects.get(name="Null author").height) # Alter the height field to NOT NULL with default old_field = Author._meta.get_field("height") new_field = PositiveIntegerField(default=42) @@ -962,10 +1077,10 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) columns = self.column_classes(Author) - self.assertFalse(columns['height'][1][6]) + self.assertFalse(columns["height"][1][6]) # Verify default value - self.assertEqual(Author.objects.get(name='Not null author').height, 12) - self.assertEqual(Author.objects.get(name='Null author').height, 42) + self.assertEqual(Author.objects.get(name="Not null author").height, 12) + self.assertEqual(Author.objects.get(name="Null author").height, 42) def test_alter_charfield_to_null(self): """ @@ -976,28 +1091,28 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Change the CharField to null - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field = copy(old_field) new_field.null = True with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific') + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_char_field_decrease_length(self): # Create the table. with connection.schema_editor() as editor: editor.create_model(Author) - Author.objects.create(name='x' * 255) + Author.objects.create(name="x" * 255) # Change max_length of CharField. - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field = CharField(max_length=254) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") with connection.schema_editor() as editor: - msg = 'value too long for type character varying(254)' + msg = "value too long for type character varying(254)" with self.assertRaisesMessage(DataError, msg): editor.alter_field(Author, old_field, new_field, strict=True) - @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific') + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_with_custom_db_type(self): from django.contrib.postgres.fields import ArrayField @@ -1005,20 +1120,20 @@ class SchemaTests(TransactionTestCase): field = ArrayField(CharField(max_length=255)) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Foo) self.isolated_local_models = [Foo] - old_field = Foo._meta.get_field('field') + old_field = Foo._meta.get_field("field") new_field = ArrayField(CharField(max_length=16)) - new_field.set_attributes_from_name('field') + new_field.set_attributes_from_name("field") new_field.model = Foo with connection.schema_editor() as editor: editor.alter_field(Foo, old_field, new_field, strict=True) - @isolate_apps('schema') - @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific') + @isolate_apps("schema") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_array_field_decrease_base_field_length(self): from django.contrib.postgres.fields import ArrayField @@ -1026,23 +1141,23 @@ class SchemaTests(TransactionTestCase): field = ArrayField(CharField(max_length=16)) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(ArrayModel) self.isolated_local_models = [ArrayModel] - ArrayModel.objects.create(field=['x' * 16]) - old_field = ArrayModel._meta.get_field('field') + ArrayModel.objects.create(field=["x" * 16]) + old_field = ArrayModel._meta.get_field("field") new_field = ArrayField(CharField(max_length=15)) - new_field.set_attributes_from_name('field') + new_field.set_attributes_from_name("field") new_field.model = ArrayModel with connection.schema_editor() as editor: - msg = 'value too long for type character varying(15)' + msg = "value too long for type character varying(15)" with self.assertRaisesMessage(DataError, msg): editor.alter_field(ArrayModel, old_field, new_field, strict=True) - @isolate_apps('schema') - @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific') + @isolate_apps("schema") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_array_field_decrease_nested_base_field_length(self): from django.contrib.postgres.fields import ArrayField @@ -1050,18 +1165,18 @@ class SchemaTests(TransactionTestCase): field = ArrayField(ArrayField(CharField(max_length=16))) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(ArrayModel) self.isolated_local_models = [ArrayModel] - ArrayModel.objects.create(field=[['x' * 16]]) - old_field = ArrayModel._meta.get_field('field') + ArrayModel.objects.create(field=[["x" * 16]]) + old_field = ArrayModel._meta.get_field("field") new_field = ArrayField(ArrayField(CharField(max_length=15))) - new_field.set_attributes_from_name('field') + new_field.set_attributes_from_name("field") new_field.model = ArrayModel with connection.schema_editor() as editor: - msg = 'value too long for type character varying(15)' + msg = "value too long for type character varying(15)" with self.assertRaisesMessage(DataError, msg): editor.alter_field(ArrayModel, old_field, new_field, strict=True) @@ -1074,7 +1189,7 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Note) # Change the TextField to null - old_field = Note._meta.get_field('info') + old_field = Note._meta.get_field("info") new_field = copy(old_field) new_field.null = True with connection.schema_editor() as editor: @@ -1090,17 +1205,19 @@ class SchemaTests(TransactionTestCase): editor.create_model(AuthorWithDefaultHeight) # Ensure the field is right to begin with columns = self.column_classes(AuthorWithDefaultHeight) - self.assertTrue(columns['height'][1][6]) + self.assertTrue(columns["height"][1][6]) # Alter the height field to NOT NULL keeping the previous default old_field = AuthorWithDefaultHeight._meta.get_field("height") new_field = PositiveIntegerField(default=42) new_field.set_attributes_from_name("height") with connection.schema_editor() as editor: - editor.alter_field(AuthorWithDefaultHeight, old_field, new_field, strict=True) + editor.alter_field( + AuthorWithDefaultHeight, old_field, new_field, strict=True + ) columns = self.column_classes(AuthorWithDefaultHeight) - self.assertFalse(columns['height'][1][6]) + self.assertFalse(columns["height"][1][6]) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_alter_fk(self): """ Tests altering of FKs @@ -1111,8 +1228,11 @@ class SchemaTests(TransactionTestCase): editor.create_model(Book) # Ensure the field is right to begin with columns = self.column_classes(Book) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) - self.assertForeignKeyExists(Book, 'author_id', 'schema_author') + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) + self.assertForeignKeyExists(Book, "author_id", "schema_author") # Alter the FK old_field = Book._meta.get_field("author") new_field = ForeignKey(Author, CASCADE, editable=False) @@ -1120,21 +1240,25 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Book, old_field, new_field, strict=True) columns = self.column_classes(Book) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) - self.assertForeignKeyExists(Book, 'author_id', 'schema_author') + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) + self.assertForeignKeyExists(Book, "author_id", "schema_author") - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_alter_to_fk(self): """ #24447 - Tests adding a FK constraint for an existing column """ + class LocalBook(Model): author = IntegerField() title = CharField(max_length=100, db_index=True) pub_date = DateTimeField() class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [LocalBook] @@ -1146,16 +1270,18 @@ class SchemaTests(TransactionTestCase): # Ensure no FK constraint exists constraints = self.get_constraints(LocalBook._meta.db_table) for details in constraints.values(): - if details['foreign_key']: - self.fail('Found an unexpected FK constraint to %s' % details['columns']) + if details["foreign_key"]: + self.fail( + "Found an unexpected FK constraint to %s" % details["columns"] + ) old_field = LocalBook._meta.get_field("author") new_field = ForeignKey(Author, CASCADE) new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(LocalBook, old_field, new_field, strict=True) - self.assertForeignKeyExists(LocalBook, 'author_id', 'schema_author') + self.assertForeignKeyExists(LocalBook, "author_id", "schema_author") - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_alter_o2o_to_fk(self): """ #24163 - Tests altering of OneToOneField to ForeignKey @@ -1166,14 +1292,21 @@ class SchemaTests(TransactionTestCase): editor.create_model(BookWithO2O) # Ensure the field is right to begin with columns = self.column_classes(BookWithO2O) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) # Ensure the field is unique author = Author.objects.create(name="Joe") - BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now()) + BookWithO2O.objects.create( + author=author, title="Django 1", pub_date=datetime.datetime.now() + ) with self.assertRaises(IntegrityError): - BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now()) + BookWithO2O.objects.create( + author=author, title="Django 2", pub_date=datetime.datetime.now() + ) BookWithO2O.objects.all().delete() - self.assertForeignKeyExists(BookWithO2O, 'author_id', 'schema_author') + self.assertForeignKeyExists(BookWithO2O, "author_id", "schema_author") # Alter the OneToOneField to ForeignKey old_field = BookWithO2O._meta.get_field("author") new_field = ForeignKey(Author, CASCADE) @@ -1181,13 +1314,20 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(BookWithO2O, old_field, new_field, strict=True) columns = self.column_classes(Book) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) # Ensure the field is not unique anymore - Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now()) - Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now()) - self.assertForeignKeyExists(Book, 'author_id', 'schema_author') + Book.objects.create( + author=author, title="Django 1", pub_date=datetime.datetime.now() + ) + Book.objects.create( + author=author, title="Django 2", pub_date=datetime.datetime.now() + ) + self.assertForeignKeyExists(Book, "author_id", "schema_author") - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_alter_fk_to_o2o(self): """ #24163 - Tests altering of ForeignKey to OneToOneField @@ -1198,13 +1338,20 @@ class SchemaTests(TransactionTestCase): editor.create_model(Book) # Ensure the field is right to begin with columns = self.column_classes(Book) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) # Ensure the field is not unique author = Author.objects.create(name="Joe") - Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now()) - Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now()) + Book.objects.create( + author=author, title="Django 1", pub_date=datetime.datetime.now() + ) + Book.objects.create( + author=author, title="Django 2", pub_date=datetime.datetime.now() + ) Book.objects.all().delete() - self.assertForeignKeyExists(Book, 'author_id', 'schema_author') + self.assertForeignKeyExists(Book, "author_id", "schema_author") # Alter the ForeignKey to OneToOneField old_field = Book._meta.get_field("author") new_field = OneToOneField(Author, CASCADE) @@ -1212,12 +1359,19 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Book, old_field, new_field, strict=True) columns = self.column_classes(BookWithO2O) - self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["author_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) # Ensure the field is unique now - BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now()) + BookWithO2O.objects.create( + author=author, title="Django 1", pub_date=datetime.datetime.now() + ) with self.assertRaises(IntegrityError): - BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now()) - self.assertForeignKeyExists(BookWithO2O, 'author_id', 'schema_author') + BookWithO2O.objects.create( + author=author, title="Django 2", pub_date=datetime.datetime.now() + ) + self.assertForeignKeyExists(BookWithO2O, "author_id", "schema_author") def test_alter_field_fk_to_o2o(self): with connection.schema_editor() as editor: @@ -1229,27 +1383,27 @@ class SchemaTests(TransactionTestCase): # Check the index is right to begin with. counts = self.get_constraints_count( Book._meta.db_table, - Book._meta.get_field('author').column, + Book._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) self.assertEqual( counts, - {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes}, + {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes}, ) - old_field = Book._meta.get_field('author') + old_field = Book._meta.get_field("author") new_field = OneToOneField(Author, CASCADE) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(Book, old_field, new_field, strict=True) counts = self.get_constraints_count( Book._meta.db_table, - Book._meta.get_field('author').column, + Book._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) # The index on ForeignKey is replaced with a unique constraint for OneToOneField. - self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0}) + self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0}) def test_alter_field_fk_keeps_index(self): with connection.schema_editor() as editor: @@ -1261,30 +1415,30 @@ class SchemaTests(TransactionTestCase): # Check the index is right to begin with. counts = self.get_constraints_count( Book._meta.db_table, - Book._meta.get_field('author').column, + Book._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) self.assertEqual( counts, - {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes}, + {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes}, ) - old_field = Book._meta.get_field('author') + old_field = Book._meta.get_field("author") # on_delete changed from CASCADE. new_field = ForeignKey(Author, PROTECT) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(Book, old_field, new_field, strict=True) counts = self.get_constraints_count( Book._meta.db_table, - Book._meta.get_field('author').column, + Book._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) # The index remains. self.assertEqual( counts, - {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes}, + {"fks": expected_fks, "uniques": 0, "indexes": expected_indexes}, ) def test_alter_field_o2o_to_fk(self): @@ -1296,24 +1450,24 @@ class SchemaTests(TransactionTestCase): # Check the unique constraint is right to begin with. counts = self.get_constraints_count( BookWithO2O._meta.db_table, - BookWithO2O._meta.get_field('author').column, + BookWithO2O._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) - self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0}) + self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0}) - old_field = BookWithO2O._meta.get_field('author') + old_field = BookWithO2O._meta.get_field("author") new_field = ForeignKey(Author, CASCADE) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(BookWithO2O, old_field, new_field, strict=True) counts = self.get_constraints_count( BookWithO2O._meta.db_table, - BookWithO2O._meta.get_field('author').column, + BookWithO2O._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) # The unique constraint on OneToOneField is replaced with an index for ForeignKey. - self.assertEqual(counts, {'fks': expected_fks, 'uniques': 0, 'indexes': 1}) + self.assertEqual(counts, {"fks": expected_fks, "uniques": 0, "indexes": 1}) def test_alter_field_o2o_keeps_unique(self): with connection.schema_editor() as editor: @@ -1324,27 +1478,27 @@ class SchemaTests(TransactionTestCase): # Check the unique constraint is right to begin with. counts = self.get_constraints_count( BookWithO2O._meta.db_table, - BookWithO2O._meta.get_field('author').column, + BookWithO2O._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) - self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0}) + self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0}) - old_field = BookWithO2O._meta.get_field('author') + old_field = BookWithO2O._meta.get_field("author") # on_delete changed from CASCADE. new_field = OneToOneField(Author, PROTECT) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.alter_field(BookWithO2O, old_field, new_field, strict=True) counts = self.get_constraints_count( BookWithO2O._meta.db_table, - BookWithO2O._meta.get_field('author').column, + BookWithO2O._meta.get_field("author").column, (Author._meta.db_table, Author._meta.pk.column), ) # The unique constraint remains. - self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0}) + self.assertEqual(counts, {"fks": expected_fks, "uniques": 1, "indexes": 0}) - @skipUnlessDBFeature('ignores_table_name_case') + @skipUnlessDBFeature("ignores_table_name_case") def test_alter_db_table_case(self): # Create the table with connection.schema_editor() as editor: @@ -1370,8 +1524,8 @@ class SchemaTests(TransactionTestCase): editor.alter_field(Author, old_field, new_field, strict=True) # This will fail if DROP DEFAULT is inadvertently executed on this # field which drops the id sequence, at least on PostgreSQL. - Author.objects.create(name='Foo') - Author.objects.create(name='Bar') + Author.objects.create(name="Foo") + Author.objects.create(name="Bar") def test_alter_autofield_pk_to_bigautofield_pk_sequence_owner(self): """ @@ -1380,20 +1534,22 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Author) - old_field = Author._meta.get_field('id') + old_field = Author._meta.get_field("id") new_field = BigAutoField(primary_key=True) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") new_field.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - Author.objects.create(name='Foo', pk=1) + Author.objects.create(name="Foo", pk=1) with connection.cursor() as cursor: - sequence_reset_sqls = connection.ops.sequence_reset_sql(no_style(), [Author]) + sequence_reset_sqls = connection.ops.sequence_reset_sql( + no_style(), [Author] + ) if sequence_reset_sqls: cursor.execute(sequence_reset_sqls[0]) # Fail on PostgreSQL if sequence is missing an owner. - self.assertIsNotNone(Author.objects.create(name='Bar')) + self.assertIsNotNone(Author.objects.create(name="Bar")) def test_alter_autofield_pk_to_smallautofield_pk_sequence_owner(self): """ @@ -1402,20 +1558,22 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Author) - old_field = Author._meta.get_field('id') + old_field = Author._meta.get_field("id") new_field = SmallAutoField(primary_key=True) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") new_field.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - Author.objects.create(name='Foo', pk=1) + Author.objects.create(name="Foo", pk=1) with connection.cursor() as cursor: - sequence_reset_sqls = connection.ops.sequence_reset_sql(no_style(), [Author]) + sequence_reset_sqls = connection.ops.sequence_reset_sql( + no_style(), [Author] + ) if sequence_reset_sqls: cursor.execute(sequence_reset_sqls[0]) # Fail on PostgreSQL if sequence is missing an owner. - self.assertIsNotNone(Author.objects.create(name='Bar')) + self.assertIsNotNone(Author.objects.create(name="Bar")) def test_alter_int_pk_to_autofield_pk(self): """ @@ -1425,10 +1583,10 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(IntegerPK) - old_field = IntegerPK._meta.get_field('i') + old_field = IntegerPK._meta.get_field("i") new_field = AutoField(primary_key=True) new_field.model = IntegerPK - new_field.set_attributes_from_name('i') + new_field.set_attributes_from_name("i") with connection.schema_editor() as editor: editor.alter_field(IntegerPK, old_field, new_field, strict=True) @@ -1439,7 +1597,7 @@ class SchemaTests(TransactionTestCase): j = IntegerField(unique=True) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps db_table = IntegerPK._meta.db_table @@ -1455,10 +1613,10 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(IntegerPK) - old_field = IntegerPK._meta.get_field('i') + old_field = IntegerPK._meta.get_field("i") new_field = BigAutoField(primary_key=True) new_field.model = IntegerPK - new_field.set_attributes_from_name('i') + new_field.set_attributes_from_name("i") with connection.schema_editor() as editor: editor.alter_field(IntegerPK, old_field, new_field, strict=True) @@ -1469,7 +1627,7 @@ class SchemaTests(TransactionTestCase): j = IntegerField(unique=True) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps db_table = IntegerPK._meta.db_table @@ -1477,25 +1635,26 @@ class SchemaTests(TransactionTestCase): obj = IntegerPKToBigAutoField.objects.create(j=1) self.assertIsNotNone(obj.i) - @isolate_apps('schema') + @isolate_apps("schema") def test_alter_smallint_pk_to_smallautofield_pk(self): """ Should be able to rename an SmallIntegerField(primary_key=True) to SmallAutoField(primary_key=True). """ + class SmallIntegerPK(Model): i = SmallIntegerField(primary_key=True) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(SmallIntegerPK) self.isolated_local_models = [SmallIntegerPK] - old_field = SmallIntegerPK._meta.get_field('i') + old_field = SmallIntegerPK._meta.get_field("i") new_field = SmallAutoField(primary_key=True) new_field.model = SmallIntegerPK - new_field.set_attributes_from_name('i') + new_field.set_attributes_from_name("i") with connection.schema_editor() as editor: editor.alter_field(SmallIntegerPK, old_field, new_field, strict=True) @@ -1507,15 +1666,15 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(IntegerPK) # Delete the old PK - old_field = IntegerPK._meta.get_field('i') + old_field = IntegerPK._meta.get_field("i") new_field = IntegerField(unique=True) new_field.model = IntegerPK - new_field.set_attributes_from_name('i') + new_field.set_attributes_from_name("i") with connection.schema_editor() as editor: editor.alter_field(IntegerPK, old_field, new_field, strict=True) # The primary key constraint is gone. Result depends on database: # 'id' for SQLite, None for others (must not be 'i'). - self.assertIn(self.get_primary_key(IntegerPK._meta.db_table), ('id', None)) + self.assertIn(self.get_primary_key(IntegerPK._meta.db_table), ("id", None)) # Set up a model class as it currently stands. The original IntegerPK # class is now out of date and some backends make use of the whole @@ -1526,15 +1685,15 @@ class SchemaTests(TransactionTestCase): j = IntegerField(unique=True) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps - db_table = 'INTEGERPK' + db_table = "INTEGERPK" # model requires a new PK - old_field = Transitional._meta.get_field('j') + old_field = Transitional._meta.get_field("j") new_field = IntegerField(primary_key=True) new_field.model = Transitional - new_field.set_attributes_from_name('j') + new_field.set_attributes_from_name("j") with connection.schema_editor() as editor: editor.alter_field(Transitional, old_field, new_field, strict=True) @@ -1545,9 +1704,9 @@ class SchemaTests(TransactionTestCase): j = IntegerField(primary_key=True) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps - db_table = 'INTEGERPK' + db_table = "INTEGERPK" # Ensure unique constraint works. IntegerUnique.objects.create(i=1, j=1) @@ -1563,7 +1722,10 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) # Ensure the field is right to begin with columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField']) + self.assertEqual( + columns["name"][0], + connection.features.introspected_field_types["CharField"], + ) self.assertNotIn("display_name", columns) # Alter the name field's name old_field = Author._meta.get_field("name") @@ -1572,34 +1734,39 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) columns = self.column_classes(Author) - self.assertEqual(columns['display_name'][0], connection.features.introspected_field_types['CharField']) + self.assertEqual( + columns["display_name"][0], + connection.features.introspected_field_types["CharField"], + ) self.assertNotIn("name", columns) - @isolate_apps('schema') + @isolate_apps("schema") def test_rename_referenced_field(self): class Author(Model): name = CharField(max_length=255, unique=True) class Meta: - app_label = 'schema' + app_label = "schema" class Book(Model): - author = ForeignKey(Author, CASCADE, to_field='name') + author = ForeignKey(Author, CASCADE, to_field="name") class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) new_field = CharField(max_length=255, unique=True) - new_field.set_attributes_from_name('renamed') - with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor: - editor.alter_field(Author, Author._meta.get_field('name'), new_field) + new_field.set_attributes_from_name("renamed") + with connection.schema_editor( + atomic=connection.features.supports_atomic_references_rename + ) as editor: + editor.alter_field(Author, Author._meta.get_field("name"), new_field) # Ensure the foreign key reference was updated. - self.assertForeignKeyExists(Book, 'author_id', 'schema_author', 'renamed') + self.assertForeignKeyExists(Book, "author_id", "schema_author", "renamed") - @skipIfDBFeature('interprets_empty_strings_as_nulls') + @skipIfDBFeature("interprets_empty_strings_as_nulls") def test_rename_keep_null_status(self): """ Renaming a field shouldn't affect the not null status. @@ -1614,7 +1781,7 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) columns = self.column_classes(Note) - self.assertEqual(columns['detail_info'][0], "TextField") + self.assertEqual(columns["detail_info"][0], "TextField") self.assertNotIn("info", columns) with self.assertRaises(IntegrityError): NoteRename.objects.create(detail_info=None) @@ -1623,6 +1790,7 @@ class SchemaTests(TransactionTestCase): """ Tests M2M fields on models during creation """ + class LocalBookWithM2M(Model): author = ForeignKey(Author, CASCADE) title = CharField(max_length=100, db_index=True) @@ -1630,8 +1798,9 @@ class SchemaTests(TransactionTestCase): tags = M2MFieldClass("TagM2MTest", related_name="books") class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps + self.local_models = [LocalBookWithM2M] # Create the tables with connection.schema_editor() as editor: @@ -1639,8 +1808,13 @@ class SchemaTests(TransactionTestCase): editor.create_model(TagM2MTest) editor.create_model(LocalBookWithM2M) # Ensure there is now an m2m table there - columns = self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through) - self.assertEqual(columns['tagm2mtest_id'][0], connection.features.introspected_field_types['IntegerField']) + columns = self.column_classes( + LocalBookWithM2M._meta.get_field("tags").remote_field.through + ) + self.assertEqual( + columns["tagm2mtest_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) def test_m2m_create(self): self._test_m2m_create(ManyToManyField) @@ -1655,19 +1829,22 @@ class SchemaTests(TransactionTestCase): """ Tests M2M fields on models during creation with through models """ + class LocalTagThrough(Model): book = ForeignKey("schema.LocalBookWithM2MThrough", CASCADE) tag = ForeignKey("schema.TagM2MTest", CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps class LocalBookWithM2MThrough(Model): - tags = M2MFieldClass("TagM2MTest", related_name="books", through=LocalTagThrough) + tags = M2MFieldClass( + "TagM2MTest", related_name="books", through=LocalTagThrough + ) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [LocalTagThrough, LocalBookWithM2MThrough] @@ -1679,8 +1856,14 @@ class SchemaTests(TransactionTestCase): editor.create_model(LocalBookWithM2MThrough) # Ensure there is now an m2m table there columns = self.column_classes(LocalTagThrough) - self.assertEqual(columns['book_id'][0], connection.features.introspected_field_types['IntegerField']) - self.assertEqual(columns['tag_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["book_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) + self.assertEqual( + columns["tag_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) def test_m2m_create_through(self): self._test_m2m_create_through(ManyToManyField) @@ -1695,11 +1878,12 @@ class SchemaTests(TransactionTestCase): """ Tests adding/removing M2M fields on models """ + class LocalAuthorWithM2M(Model): name = CharField(max_length=255) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [LocalAuthorWithM2M] @@ -1719,7 +1903,10 @@ class SchemaTests(TransactionTestCase): editor.add_field(LocalAuthorWithM2M, new_field) # Ensure there is now an m2m table there columns = self.column_classes(new_field.remote_field.through) - self.assertEqual(columns['tagm2mtest_id'][0], connection.features.introspected_field_types['IntegerField']) + self.assertEqual( + columns["tagm2mtest_id"][0], + connection.features.introspected_field_types["IntegerField"], + ) # "Alter" the field. This should not rename the DB table to itself. with connection.schema_editor() as editor: @@ -1736,7 +1923,9 @@ class SchemaTests(TransactionTestCase): # we've removed the tags field. opts = LocalAuthorWithM2M._meta opts.local_many_to_many.remove(new_field) - del new_apps.all_models['schema'][new_field.remote_field.through._meta.model_name] + del new_apps.all_models["schema"][ + new_field.remote_field.through._meta.model_name + ] opts._expire_cache() def test_m2m(self): @@ -1752,20 +1941,23 @@ class SchemaTests(TransactionTestCase): """ Tests altering M2Ms with explicit through models (should no-op) """ + class LocalAuthorTag(Model): author = ForeignKey("schema.LocalAuthorWithM2MThrough", CASCADE) tag = ForeignKey("schema.TagM2MTest", CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps class LocalAuthorWithM2MThrough(Model): name = CharField(max_length=255) - tags = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag) + tags = M2MFieldClass( + "schema.TagM2MTest", related_name="authors", through=LocalAuthorTag + ) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps self.local_models = [LocalAuthorTag, LocalAuthorWithM2MThrough] @@ -1779,10 +1971,14 @@ class SchemaTests(TransactionTestCase): self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3) # "Alter" the field's blankness. This should not actually do anything. old_field = LocalAuthorWithM2MThrough._meta.get_field("tags") - new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag) + new_field = M2MFieldClass( + "schema.TagM2MTest", related_name="authors", through=LocalAuthorTag + ) new_field.contribute_to_class(LocalAuthorWithM2MThrough, "tags") with connection.schema_editor() as editor: - editor.alter_field(LocalAuthorWithM2MThrough, old_field, new_field, strict=True) + editor.alter_field( + LocalAuthorWithM2MThrough, old_field, new_field, strict=True + ) # Ensure the m2m table is still there self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3) @@ -1799,6 +1995,7 @@ class SchemaTests(TransactionTestCase): """ Tests repointing M2M fields """ + class LocalBookWithM2M(Model): author = ForeignKey(Author, CASCADE) title = CharField(max_length=100, db_index=True) @@ -1806,8 +2003,9 @@ class SchemaTests(TransactionTestCase): tags = M2MFieldClass("TagM2MTest", related_name="books") class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps + self.local_models = [LocalBookWithM2M] # Create the tables with connection.schema_editor() as editor: @@ -1819,8 +2017,8 @@ class SchemaTests(TransactionTestCase): if connection.features.supports_foreign_keys: self.assertForeignKeyExists( LocalBookWithM2M._meta.get_field("tags").remote_field.through, - 'tagm2mtest_id', - 'schema_tagm2mtest', + "tagm2mtest_id", + "schema_tagm2mtest", ) # Repoint the M2M old_field = LocalBookWithM2M._meta.get_field("tags") @@ -1830,14 +2028,18 @@ class SchemaTests(TransactionTestCase): editor.alter_field(LocalBookWithM2M, old_field, new_field, strict=True) # Ensure old M2M is gone with self.assertRaises(DatabaseError): - self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through) + self.column_classes( + LocalBookWithM2M._meta.get_field("tags").remote_field.through + ) # This model looks like the new model and is used for teardown. opts = LocalBookWithM2M._meta opts.local_many_to_many.remove(old_field) # Ensure the new M2M exists and points to UniqueTest if connection.features.supports_foreign_keys: - self.assertForeignKeyExists(new_field.remote_field.through, 'uniquetest_id', 'schema_uniquetest') + self.assertForeignKeyExists( + new_field.remote_field.through, "uniquetest_id", "schema_uniquetest" + ) def test_m2m_repoint(self): self._test_m2m_repoint(ManyToManyField) @@ -1848,19 +2050,19 @@ class SchemaTests(TransactionTestCase): def test_m2m_repoint_inherited(self): self._test_m2m_repoint(InheritedManyToManyField) - @isolate_apps('schema') + @isolate_apps("schema") def test_m2m_rename_field_in_target_model(self): class LocalTagM2MTest(Model): title = CharField(max_length=255) class Meta: - app_label = 'schema' + app_label = "schema" class LocalM2M(Model): tags = ManyToManyField(LocalTagM2MTest) class Meta: - app_label = 'schema' + app_label = "schema" # Create the tables. with connection.schema_editor() as editor: @@ -1870,9 +2072,9 @@ class SchemaTests(TransactionTestCase): # Ensure the m2m table is there. self.assertEqual(len(self.column_classes(LocalM2M)), 1) # Alter a field in LocalTagM2MTest. - old_field = LocalTagM2MTest._meta.get_field('title') + old_field = LocalTagM2MTest._meta.get_field("title") new_field = CharField(max_length=254) - new_field.contribute_to_class(LocalTagM2MTest, 'title1') + new_field.contribute_to_class(LocalTagM2MTest, "title1") # @isolate_apps() and inner models are needed to have the model # relations populated, otherwise this doesn't act as a regression test. self.assertEqual(len(new_field.model._meta.related_objects), 1) @@ -1881,7 +2083,9 @@ class SchemaTests(TransactionTestCase): # Ensure the m2m table is still there. self.assertEqual(len(self.column_classes(LocalM2M)), 1) - @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') + @skipUnlessDBFeature( + "supports_column_check_constraints", "can_introspect_check_constraints" + ) def test_check_constraints(self): """ Tests creating/deleting CHECK constraints @@ -1891,7 +2095,10 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) # Ensure the constraint exists constraints = self.get_constraints(Author._meta.db_table) - if not any(details['columns'] == ['height'] and details['check'] for details in constraints.values()): + if not any( + details["columns"] == ["height"] and details["check"] + for details in constraints.values() + ): self.fail("No check constraint for height found") # Alter the column to remove it old_field = Author._meta.get_field("height") @@ -1901,29 +2108,34 @@ class SchemaTests(TransactionTestCase): editor.alter_field(Author, old_field, new_field, strict=True) constraints = self.get_constraints(Author._meta.db_table) for details in constraints.values(): - if details['columns'] == ["height"] and details['check']: + if details["columns"] == ["height"] and details["check"]: self.fail("Check constraint for height found") # Alter the column to re-add it new_field2 = Author._meta.get_field("height") with connection.schema_editor() as editor: editor.alter_field(Author, new_field, new_field2, strict=True) constraints = self.get_constraints(Author._meta.db_table) - if not any(details['columns'] == ['height'] and details['check'] for details in constraints.values()): + if not any( + details["columns"] == ["height"] and details["check"] + for details in constraints.values() + ): self.fail("No check constraint for height found") - @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') - @isolate_apps('schema') + @skipUnlessDBFeature( + "supports_column_check_constraints", "can_introspect_check_constraints" + ) + @isolate_apps("schema") def test_check_constraint_timedelta_param(self): class DurationModel(Model): duration = DurationField() class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(DurationModel) self.isolated_local_models = [DurationModel] - constraint_name = 'duration_gte_5_minutes' + constraint_name = "duration_gte_5_minutes" constraint = CheckConstraint( check=Q(duration__gt=datetime.timedelta(minutes=5)), name=constraint_name, @@ -1937,12 +2149,16 @@ class SchemaTests(TransactionTestCase): DurationModel.objects.create(duration=datetime.timedelta(minutes=4)) DurationModel.objects.create(duration=datetime.timedelta(minutes=10)) - @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints') + @skipUnlessDBFeature( + "supports_column_check_constraints", "can_introspect_check_constraints" + ) def test_remove_field_check_does_not_remove_meta_constraints(self): with connection.schema_editor() as editor: editor.create_model(Author) # Add the custom check constraint - constraint = CheckConstraint(check=Q(height__gte=0), name='author_height_gte_0_check') + constraint = CheckConstraint( + check=Q(height__gte=0), name="author_height_gte_0_check" + ) custom_constraint_name = constraint.name Author._meta.constraints = [constraint] with connection.schema_editor() as editor: @@ -1951,32 +2167,41 @@ class SchemaTests(TransactionTestCase): constraints = self.get_constraints(Author._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["height"] + and details["check"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Alter the column to remove field check - old_field = Author._meta.get_field('height') + old_field = Author._meta.get_field("height") new_field = IntegerField(null=True, blank=True) - new_field.set_attributes_from_name('height') + new_field.set_attributes_from_name("height") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) constraints = self.get_constraints(Author._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["height"] + and details["check"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 0) # Alter the column to re-add field check - new_field2 = Author._meta.get_field('height') + new_field2 = Author._meta.get_field("height") with connection.schema_editor() as editor: editor.alter_field(Author, new_field, new_field2, strict=True) constraints = self.get_constraints(Author._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["height"] + and details["check"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Drop the check constraint @@ -2032,15 +2257,15 @@ class SchemaTests(TransactionTestCase): try: with connection.schema_editor() as editor: editor.create_model(TagUniqueRename) - editor.alter_db_table(TagUniqueRename, old_table_name, 'unique-table') - TagUniqueRename._meta.db_table = 'unique-table' + editor.alter_db_table(TagUniqueRename, old_table_name, "unique-table") + TagUniqueRename._meta.db_table = "unique-table" # This fails if the unique index name isn't quoted. - editor.alter_unique_together(TagUniqueRename, [], (('title', 'slug2'),)) + editor.alter_unique_together(TagUniqueRename, [], (("title", "slug2"),)) finally: TagUniqueRename._meta.db_table = old_table_name - @isolate_apps('schema') - @skipUnlessDBFeature('supports_foreign_keys') + @isolate_apps("schema") + @skipUnlessDBFeature("supports_foreign_keys") def test_unique_no_unnecessary_fk_drops(self): """ If AlterField isn't selective about dropping foreign key constraints @@ -2048,72 +2273,74 @@ class SchemaTests(TransactionTestCase): incorrectly drops and recreates the Book.author foreign key even though it doesn't restrict the field being changed (#29193). """ + class Author(Model): name = CharField(max_length=254, unique=True) class Meta: - app_label = 'schema' + app_label = "schema" class Book(Model): author = ForeignKey(Author, CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) new_field = CharField(max_length=255, unique=True) new_field.model = Author - new_field.set_attributes_from_name('name') - with self.assertLogs('django.db.backends.schema', 'DEBUG') as cm: + new_field.set_attributes_from_name("name") + with self.assertLogs("django.db.backends.schema", "DEBUG") as cm: with connection.schema_editor() as editor: - editor.alter_field(Author, Author._meta.get_field('name'), new_field) + editor.alter_field(Author, Author._meta.get_field("name"), new_field) # One SQL statement is executed to alter the field. self.assertEqual(len(cm.records), 1) - @isolate_apps('schema') + @isolate_apps("schema") def test_unique_and_reverse_m2m(self): """ AlterField can modify a unique field when there's a reverse M2M relation on the model. """ + class Tag(Model): title = CharField(max_length=255) slug = SlugField(unique=True) class Meta: - app_label = 'schema' + app_label = "schema" class Book(Model): - tags = ManyToManyField(Tag, related_name='books') + tags = ManyToManyField(Tag, related_name="books") class Meta: - app_label = 'schema' + app_label = "schema" - self.isolated_local_models = [Book._meta.get_field('tags').remote_field.through] + self.isolated_local_models = [Book._meta.get_field("tags").remote_field.through] with connection.schema_editor() as editor: editor.create_model(Tag) editor.create_model(Book) new_field = SlugField(max_length=75, unique=True) new_field.model = Tag - new_field.set_attributes_from_name('slug') - with self.assertLogs('django.db.backends.schema', 'DEBUG') as cm: + new_field.set_attributes_from_name("slug") + with self.assertLogs("django.db.backends.schema", "DEBUG") as cm: with connection.schema_editor() as editor: - editor.alter_field(Tag, Tag._meta.get_field('slug'), new_field) + editor.alter_field(Tag, Tag._meta.get_field("slug"), new_field) # One SQL statement is executed to alter the field. self.assertEqual(len(cm.records), 1) # Ensure that the field is still unique. - Tag.objects.create(title='foo', slug='foo') + Tag.objects.create(title="foo", slug="foo") with self.assertRaises(IntegrityError): - Tag.objects.create(title='bar', slug='foo') + Tag.objects.create(title="bar", slug="foo") - @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields') + @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields") def test_remove_field_unique_does_not_remove_meta_constraints(self): with connection.schema_editor() as editor: editor.create_model(AuthorWithUniqueName) # Add the custom unique constraint - constraint = UniqueConstraint(fields=['name'], name='author_name_uniq') + constraint = UniqueConstraint(fields=["name"], name="author_name_uniq") custom_constraint_name = constraint.name AuthorWithUniqueName._meta.constraints = [constraint] with connection.schema_editor() as editor: @@ -2122,32 +2349,41 @@ class SchemaTests(TransactionTestCase): constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Alter the column to remove field uniqueness - old_field = AuthorWithUniqueName._meta.get_field('name') + old_field = AuthorWithUniqueName._meta.get_field("name") new_field = CharField(max_length=255) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") with connection.schema_editor() as editor: editor.alter_field(AuthorWithUniqueName, old_field, new_field, strict=True) constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 0) # Alter the column to re-add field uniqueness - new_field2 = AuthorWithUniqueName._meta.get_field('name') + new_field2 = AuthorWithUniqueName._meta.get_field("name") with connection.schema_editor() as editor: editor.alter_field(AuthorWithUniqueName, new_field, new_field2, strict=True) constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Drop the unique constraint @@ -2171,7 +2407,9 @@ class SchemaTests(TransactionTestCase): UniqueTest.objects.all().delete() # Alter the model to its non-unique-together companion with connection.schema_editor() as editor: - editor.alter_unique_together(UniqueTest, UniqueTest._meta.unique_together, []) + editor.alter_unique_together( + UniqueTest, UniqueTest._meta.unique_together, [] + ) # Ensure the fields are no longer unique UniqueTest.objects.create(year=2012, slug="foo") UniqueTest.objects.create(year=2012, slug="foo") @@ -2180,7 +2418,9 @@ class SchemaTests(TransactionTestCase): new_field2 = SlugField(unique=True) new_field2.set_attributes_from_name("slug") with connection.schema_editor() as editor: - editor.alter_unique_together(UniqueTest, [], UniqueTest._meta.unique_together) + editor.alter_unique_together( + UniqueTest, [], UniqueTest._meta.unique_together + ) # Ensure the fields are unique again UniqueTest.objects.create(year=2012, slug="foo") with self.assertRaises(IntegrityError): @@ -2200,10 +2440,10 @@ class SchemaTests(TransactionTestCase): self.assertEqual(Book._meta.unique_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: - editor.alter_unique_together(Book, [], [['author', 'title']]) + editor.alter_unique_together(Book, [], [["author", "title"]]) # Alter it back with connection.schema_editor() as editor: - editor.alter_unique_together(Book, [['author', 'title']], []) + editor.alter_unique_together(Book, [["author", "title"]], []) def test_unique_together_with_fk_with_existing_index(self): """ @@ -2216,54 +2456,75 @@ class SchemaTests(TransactionTestCase): editor.create_model(Author) editor.create_model(BookWithoutAuthor) new_field = ForeignKey(Author, CASCADE) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") editor.add_field(BookWithoutAuthor, new_field) # Ensure the fields aren't unique to begin with self.assertEqual(Book._meta.unique_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: - editor.alter_unique_together(Book, [], [['author', 'title']]) + editor.alter_unique_together(Book, [], [["author", "title"]]) # Alter it back with connection.schema_editor() as editor: - editor.alter_unique_together(Book, [['author', 'title']], []) + editor.alter_unique_together(Book, [["author", "title"]], []) - @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields') + @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields") def test_remove_unique_together_does_not_remove_meta_constraints(self): with connection.schema_editor() as editor: editor.create_model(AuthorWithUniqueNameAndBirthday) # Add the custom unique constraint - constraint = UniqueConstraint(fields=['name', 'birthday'], name='author_name_birthday_uniq') + constraint = UniqueConstraint( + fields=["name", "birthday"], name="author_name_birthday_uniq" + ) custom_constraint_name = constraint.name AuthorWithUniqueNameAndBirthday._meta.constraints = [constraint] with connection.schema_editor() as editor: editor.add_constraint(AuthorWithUniqueNameAndBirthday, constraint) # Ensure the constraints exist - constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table) + constraints = self.get_constraints( + AuthorWithUniqueNameAndBirthday._meta.db_table + ) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Remove unique together unique_together = AuthorWithUniqueNameAndBirthday._meta.unique_together with connection.schema_editor() as editor: - editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, unique_together, []) - constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table) + editor.alter_unique_together( + AuthorWithUniqueNameAndBirthday, unique_together, [] + ) + constraints = self.get_constraints( + AuthorWithUniqueNameAndBirthday._meta.db_table + ) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 0) # Re-add unique together with connection.schema_editor() as editor: - editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, [], unique_together) - constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table) + editor.alter_unique_together( + AuthorWithUniqueNameAndBirthday, [], unique_together + ) + constraints = self.get_constraints( + AuthorWithUniqueNameAndBirthday._meta.db_table + ) self.assertIn(custom_constraint_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["unique"] + and name != custom_constraint_name ] self.assertEqual(len(other_constraints), 1) # Drop the unique constraint @@ -2274,24 +2535,24 @@ class SchemaTests(TransactionTestCase): def test_unique_constraint(self): with connection.schema_editor() as editor: editor.create_model(Author) - constraint = UniqueConstraint(fields=['name'], name='name_uq') + constraint = UniqueConstraint(fields=["name"], name="name_uq") # Add constraint. with connection.schema_editor() as editor: editor.add_constraint(Author, constraint) sql = constraint.create_sql(Author, editor) table = Author._meta.db_table self.assertIs(sql.references_table(table), True) - self.assertIs(sql.references_column(table, 'name'), True) + self.assertIs(sql.references_column(table, "name"), True) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint(self): with connection.schema_editor() as editor: editor.create_model(Author) - constraint = UniqueConstraint(Upper('name').desc(), name='func_upper_uq') + constraint = UniqueConstraint(Upper("name").desc(), name="func_upper_uq") # Add constraint. with connection.schema_editor() as editor: editor.add_constraint(Author, constraint) @@ -2299,26 +2560,26 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table constraints = self.get_constraints(table) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, constraint.name, ['DESC']) + self.assertIndexOrder(table, constraint.name, ["DESC"]) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) + self.assertIs(constraints[constraint.name]["unique"], True) # SQL contains a database function. - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql)) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql)) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_composite_func_unique_constraint(self): with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(BookWithSlug) constraint = UniqueConstraint( - Upper('title'), - Lower('slug'), - name='func_upper_lower_unq', + Upper("title"), + Lower("slug"), + name="func_upper_lower_unq", ) # Add constraint. with connection.schema_editor() as editor: @@ -2327,28 +2588,28 @@ class SchemaTests(TransactionTestCase): table = BookWithSlug._meta.db_table constraints = self.get_constraints(table) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) + self.assertIs(constraints[constraint.name]["unique"], True) # SQL contains database functions. - self.assertIs(sql.references_column(table, 'title'), True) - self.assertIs(sql.references_column(table, 'slug'), True) + self.assertIs(sql.references_column(table, "title"), True) + self.assertIs(sql.references_column(table, "slug"), True) sql = str(sql) - self.assertIn('UPPER(%s)' % editor.quote_name('title'), sql) - self.assertIn('LOWER(%s)' % editor.quote_name('slug'), sql) - self.assertLess(sql.index('UPPER'), sql.index('LOWER')) + self.assertIn("UPPER(%s)" % editor.quote_name("title"), sql) + self.assertIn("LOWER(%s)" % editor.quote_name("slug"), sql) + self.assertLess(sql.index("UPPER"), sql.index("LOWER")) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(BookWithSlug, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_unique_constraint_field_and_expression(self): with connection.schema_editor() as editor: editor.create_model(Author) constraint = UniqueConstraint( - F('height').desc(), - 'uuid', - Lower('name').asc(), - name='func_f_lower_field_unq', + F("height").desc(), + "uuid", + Lower("name").asc(), + name="func_f_lower_field_unq", ) # Add constraint. with connection.schema_editor() as editor: @@ -2356,28 +2617,28 @@ class SchemaTests(TransactionTestCase): sql = constraint.create_sql(Author, editor) table = Author._meta.db_table if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, constraint.name, ['DESC', 'ASC', 'ASC']) + self.assertIndexOrder(table, constraint.name, ["DESC", "ASC", "ASC"]) constraints = self.get_constraints(table) - self.assertIs(constraints[constraint.name]['unique'], True) - self.assertEqual(len(constraints[constraint.name]['columns']), 3) - self.assertEqual(constraints[constraint.name]['columns'][1], 'uuid') + self.assertIs(constraints[constraint.name]["unique"], True) + self.assertEqual(len(constraints[constraint.name]["columns"]), 3) + self.assertEqual(constraints[constraint.name]["columns"][1], "uuid") # SQL contains database functions and columns. - self.assertIs(sql.references_column(table, 'height'), True) - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIs(sql.references_column(table, 'uuid'), True) - self.assertIn('LOWER(%s)' % editor.quote_name('name'), str(sql)) + self.assertIs(sql.references_column(table, "height"), True) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIs(sql.references_column(table, "uuid"), True) + self.assertIn("LOWER(%s)" % editor.quote_name("name"), str(sql)) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes', 'supports_partial_indexes') + @skipUnlessDBFeature("supports_expression_indexes", "supports_partial_indexes") def test_func_unique_constraint_partial(self): with connection.schema_editor() as editor: editor.create_model(Author) constraint = UniqueConstraint( - Upper('name'), - name='func_upper_cond_weight_uq', + Upper("name"), + name="func_upper_cond_weight_uq", condition=Q(weight__isnull=False), ) # Add constraint. @@ -2387,11 +2648,11 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table constraints = self.get_constraints(table) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql)) + self.assertIs(constraints[constraint.name]["unique"], True) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql)) self.assertIn( - 'WHERE %s IS NOT NULL' % editor.quote_name('weight'), + "WHERE %s IS NOT NULL" % editor.quote_name("weight"), str(sql), ) # Remove constraint. @@ -2399,14 +2660,14 @@ class SchemaTests(TransactionTestCase): editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes', 'supports_covering_indexes') + @skipUnlessDBFeature("supports_expression_indexes", "supports_covering_indexes") def test_func_unique_constraint_covering(self): with connection.schema_editor() as editor: editor.create_model(Author) constraint = UniqueConstraint( - Upper('name'), - name='func_upper_covering_uq', - include=['weight', 'height'], + Upper("name"), + name="func_upper_covering_uq", + include=["weight", "height"], ) # Add constraint. with connection.schema_editor() as editor: @@ -2415,19 +2676,20 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table constraints = self.get_constraints(table) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) + self.assertIs(constraints[constraint.name]["unique"], True) self.assertEqual( - constraints[constraint.name]['columns'], - [None, 'weight', 'height'], + constraints[constraint.name]["columns"], + [None, "weight", "height"], ) - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIs(sql.references_column(table, 'weight'), True) - self.assertIs(sql.references_column(table, 'height'), True) - self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql)) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIs(sql.references_column(table, "weight"), True) + self.assertIs(sql.references_column(table, "height"), True) + self.assertIn("UPPER(%s)" % editor.quote_name("name"), str(sql)) self.assertIn( - 'INCLUDE (%s, %s)' % ( - editor.quote_name('weight'), - editor.quote_name('height'), + "INCLUDE (%s, %s)" + % ( + editor.quote_name("weight"), + editor.quote_name("height"), ), str(sql), ) @@ -2436,15 +2698,15 @@ class SchemaTests(TransactionTestCase): editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_lookups(self): with connection.schema_editor() as editor: editor.create_model(Author) with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs): constraint = UniqueConstraint( - F('name__lower'), - F('weight__abs'), - name='func_lower_abs_lookup_uq', + F("name__lower"), + F("weight__abs"), + name="func_lower_abs_lookup_uq", ) # Add constraint. with connection.schema_editor() as editor: @@ -2453,29 +2715,27 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table constraints = self.get_constraints(table) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) + self.assertIs(constraints[constraint.name]["unique"], True) # SQL contains columns. - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIs(sql.references_column(table, 'weight'), True) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIs(sql.references_column(table, "weight"), True) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(Author, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_collate(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest( - 'This backend does not support case-insensitive collations.' - ) + self.skipTest("This backend does not support case-insensitive collations.") with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(BookWithSlug) constraint = UniqueConstraint( - Collate(F('title'), collation=collation).desc(), - Collate('slug', collation=collation), - name='func_collate_uq', + Collate(F("title"), collation=collation).desc(), + Collate("slug", collation=collation), + name="func_collate_uq", ) # Add constraint. with connection.schema_editor() as editor: @@ -2484,32 +2744,32 @@ class SchemaTests(TransactionTestCase): table = BookWithSlug._meta.db_table constraints = self.get_constraints(table) self.assertIn(constraint.name, constraints) - self.assertIs(constraints[constraint.name]['unique'], True) + self.assertIs(constraints[constraint.name]["unique"], True) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, constraint.name, ['DESC', 'ASC']) + self.assertIndexOrder(table, constraint.name, ["DESC", "ASC"]) # SQL contains columns and a collation. - self.assertIs(sql.references_column(table, 'title'), True) - self.assertIs(sql.references_column(table, 'slug'), True) - self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql)) + self.assertIs(sql.references_column(table, "title"), True) + self.assertIs(sql.references_column(table, "slug"), True) + self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql)) # Remove constraint. with connection.schema_editor() as editor: editor.remove_constraint(BookWithSlug, constraint) self.assertNotIn(constraint.name, self.get_constraints(table)) - @skipIfDBFeature('supports_expression_indexes') + @skipIfDBFeature("supports_expression_indexes") def test_func_unique_constraint_unsupported(self): # UniqueConstraint is ignored on databases that don't support indexes on # expressions. with connection.schema_editor() as editor: editor.create_model(Author) - constraint = UniqueConstraint(F('name'), name='func_name_uq') + constraint = UniqueConstraint(F("name"), name="func_name_uq") with connection.schema_editor() as editor, self.assertNumQueries(0): self.assertIsNone(editor.add_constraint(Author, constraint)) self.assertIsNone(editor.remove_constraint(Author, constraint)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_nonexistent_field(self): - constraint = UniqueConstraint(Lower('nonexistent'), name='func_nonexistent_uq') + constraint = UniqueConstraint(Lower("nonexistent"), name="func_nonexistent_uq") msg = ( "Cannot resolve keyword 'nonexistent' into field. Choices are: " "height, id, name, uuid, weight" @@ -2518,11 +2778,11 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.add_constraint(Author, constraint) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_unique_constraint_nondeterministic(self): with connection.schema_editor() as editor: editor.create_model(Author) - constraint = UniqueConstraint(Random(), name='func_random_uq') + constraint = UniqueConstraint(Random(), name="func_random_uq") with connection.schema_editor() as editor: with self.assertRaises(DatabaseError): editor.add_constraint(Author, constraint) @@ -2539,7 +2799,7 @@ class SchemaTests(TransactionTestCase): any( c["index"] for c in self.get_constraints("schema_tag").values() - if c['columns'] == ["slug", "title"] + if c["columns"] == ["slug", "title"] ), False, ) @@ -2551,7 +2811,7 @@ class SchemaTests(TransactionTestCase): any( c["index"] for c in self.get_constraints("schema_tag").values() - if c['columns'] == ["slug", "title"] + if c["columns"] == ["slug", "title"] ), True, ) @@ -2565,7 +2825,7 @@ class SchemaTests(TransactionTestCase): any( c["index"] for c in self.get_constraints("schema_tag").values() - if c['columns'] == ["slug", "title"] + if c["columns"] == ["slug", "title"] ), False, ) @@ -2583,10 +2843,10 @@ class SchemaTests(TransactionTestCase): self.assertEqual(Book._meta.index_together, ()) # Add the unique_together constraint with connection.schema_editor() as editor: - editor.alter_index_together(Book, [], [['author', 'title']]) + editor.alter_index_together(Book, [], [["author", "title"]]) # Alter it back with connection.schema_editor() as editor: - editor.alter_index_together(Book, [['author', 'title']], []) + editor.alter_index_together(Book, [["author", "title"]], []) def test_create_index_together(self): """ @@ -2600,48 +2860,67 @@ class SchemaTests(TransactionTestCase): any( c["index"] for c in self.get_constraints("schema_tagindexed").values() - if c['columns'] == ["slug", "title"] + if c["columns"] == ["slug", "title"] ), True, ) - @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields') + @skipUnlessDBFeature("allows_multiple_constraints_on_same_fields") def test_remove_index_together_does_not_remove_meta_indexes(self): with connection.schema_editor() as editor: editor.create_model(AuthorWithIndexedNameAndBirthday) # Add the custom index - index = Index(fields=['name', 'birthday'], name='author_name_birthday_idx') + index = Index(fields=["name", "birthday"], name="author_name_birthday_idx") custom_index_name = index.name AuthorWithIndexedNameAndBirthday._meta.indexes = [index] with connection.schema_editor() as editor: editor.add_index(AuthorWithIndexedNameAndBirthday, index) # Ensure the indexes exist - constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table) + constraints = self.get_constraints( + AuthorWithIndexedNameAndBirthday._meta.db_table + ) self.assertIn(custom_index_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["index"] + and name != custom_index_name ] self.assertEqual(len(other_constraints), 1) # Remove index together index_together = AuthorWithIndexedNameAndBirthday._meta.index_together with connection.schema_editor() as editor: - editor.alter_index_together(AuthorWithIndexedNameAndBirthday, index_together, []) - constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table) + editor.alter_index_together( + AuthorWithIndexedNameAndBirthday, index_together, [] + ) + constraints = self.get_constraints( + AuthorWithIndexedNameAndBirthday._meta.db_table + ) self.assertIn(custom_index_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["index"] + and name != custom_index_name ] self.assertEqual(len(other_constraints), 0) # Re-add index together with connection.schema_editor() as editor: - editor.alter_index_together(AuthorWithIndexedNameAndBirthday, [], index_together) - constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table) + editor.alter_index_together( + AuthorWithIndexedNameAndBirthday, [], index_together + ) + constraints = self.get_constraints( + AuthorWithIndexedNameAndBirthday._meta.db_table + ) self.assertIn(custom_index_name, constraints) other_constraints = [ - name for name, details in constraints.items() - if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name + name + for name, details in constraints.items() + if details["columns"] == ["name", "birthday"] + and details["index"] + and name != custom_index_name ] self.assertEqual(len(other_constraints), 1) # Drop the index @@ -2649,22 +2928,23 @@ class SchemaTests(TransactionTestCase): AuthorWithIndexedNameAndBirthday._meta.indexes = [] editor.remove_index(AuthorWithIndexedNameAndBirthday, index) - @isolate_apps('schema') + @isolate_apps("schema") def test_db_table(self): """ Tests renaming of the table """ + class Author(Model): name = CharField(max_length=255) class Meta: - app_label = 'schema' + app_label = "schema" class Book(Model): author = ForeignKey(Author, CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" # Create the table and one referring it. with connection.schema_editor() as editor: @@ -2672,22 +2952,35 @@ class SchemaTests(TransactionTestCase): editor.create_model(Book) # Ensure the table is there to begin with columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField']) + self.assertEqual( + columns["name"][0], + connection.features.introspected_field_types["CharField"], + ) # Alter the table - with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor: + with connection.schema_editor( + atomic=connection.features.supports_atomic_references_rename + ) as editor: editor.alter_db_table(Author, "schema_author", "schema_otherauthor") Author._meta.db_table = "schema_otherauthor" columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField']) + self.assertEqual( + columns["name"][0], + connection.features.introspected_field_types["CharField"], + ) # Ensure the foreign key reference was updated self.assertForeignKeyExists(Book, "author_id", "schema_otherauthor") # Alter the table again - with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor: + with connection.schema_editor( + atomic=connection.features.supports_atomic_references_rename + ) as editor: editor.alter_db_table(Author, "schema_otherauthor", "schema_author") # Ensure the table is still there Author._meta.db_table = "schema_author" columns = self.column_classes(Author) - self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField']) + self.assertEqual( + columns["name"][0], + connection.features.introspected_field_types["CharField"], + ) def test_add_remove_index(self): """ @@ -2697,16 +2990,16 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Ensure the table is there and has no index - self.assertNotIn('title', self.get_indexes(Author._meta.db_table)) + self.assertNotIn("title", self.get_indexes(Author._meta.db_table)) # Add the index - index = Index(fields=['name'], name='author_title_idx') + index = Index(fields=["name"], name="author_title_idx") with connection.schema_editor() as editor: editor.add_index(Author, index) - self.assertIn('name', self.get_indexes(Author._meta.db_table)) + self.assertIn("name", self.get_indexes(Author._meta.db_table)) # Drop the index with connection.schema_editor() as editor: editor.remove_index(Author, index) - self.assertNotIn('name', self.get_indexes(Author._meta.db_table)) + self.assertNotIn("name", self.get_indexes(Author._meta.db_table)) def test_remove_db_index_doesnt_remove_custom_indexes(self): """ @@ -2715,15 +3008,15 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(AuthorWithIndexedName) # Ensure the table has its index - self.assertIn('name', self.get_indexes(AuthorWithIndexedName._meta.db_table)) + self.assertIn("name", self.get_indexes(AuthorWithIndexedName._meta.db_table)) # Add the custom index - index = Index(fields=['-name'], name='author_name_idx') + index = Index(fields=["-name"], name="author_name_idx") author_index_name = index.name with connection.schema_editor() as editor: db_index_name = editor._create_index_name( table_name=AuthorWithIndexedName._meta.db_table, - column_names=('name',), + column_names=("name",), ) try: AuthorWithIndexedName._meta.indexes = [index] @@ -2733,11 +3026,13 @@ class SchemaTests(TransactionTestCase): self.assertIn(author_index_name, old_constraints) self.assertIn(db_index_name, old_constraints) # Change name field to db_index=False - old_field = AuthorWithIndexedName._meta.get_field('name') + old_field = AuthorWithIndexedName._meta.get_field("name") new_field = CharField(max_length=255) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") with connection.schema_editor() as editor: - editor.alter_field(AuthorWithIndexedName, old_field, new_field, strict=True) + editor.alter_field( + AuthorWithIndexedName, old_field, new_field, strict=True + ) new_constraints = self.get_constraints(AuthorWithIndexedName._meta.db_table) self.assertNotIn(db_index_name, new_constraints) # The index from Meta.indexes is still in the database. @@ -2755,14 +3050,14 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # The table doesn't have an index - self.assertNotIn('title', self.get_indexes(Author._meta.db_table)) - index_name = 'author_name_idx' + self.assertNotIn("title", self.get_indexes(Author._meta.db_table)) + index_name = "author_name_idx" # Add the index - index = Index(fields=['name', '-weight'], name=index_name) + index = Index(fields=["name", "-weight"], name=index_name) with connection.schema_editor() as editor: editor.add_index(Author, index) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(Author._meta.db_table, index_name, ['ASC', 'DESC']) + self.assertIndexOrder(Author._meta.db_table, index_name, ["ASC", "DESC"]) # Drop the index with connection.schema_editor() as editor: editor.remove_index(Author, index) @@ -2822,19 +3117,28 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(AuthorTextFieldWithIndex) # The text_field index is present if the database supports it. - assertion = self.assertIn if connection.features.supports_index_on_text_field else self.assertNotIn - assertion('text_field', self.get_indexes(AuthorTextFieldWithIndex._meta.db_table)) + assertion = ( + self.assertIn + if connection.features.supports_index_on_text_field + else self.assertNotIn + ) + assertion( + "text_field", self.get_indexes(AuthorTextFieldWithIndex._meta.db_table) + ) def _index_expressions_wrappers(self): index_expression = IndexExpression() index_expression.set_wrapper_classes(connection) - return ', '.join([ - wrapper_cls.__qualname__ for wrapper_cls in index_expression.wrapper_classes - ]) + return ", ".join( + [ + wrapper_cls.__qualname__ + for wrapper_cls in index_expression.wrapper_classes + ] + ) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_multiple_wrapper_references(self): - index = Index(OrderBy(F('name').desc(), descending=True), name='name') + index = Index(OrderBy(F("name").desc(), descending=True), name="name") msg = ( "Multiple references to %s can't be used in an indexed expression." % self._index_expressions_wrappers() @@ -2843,42 +3147,42 @@ class SchemaTests(TransactionTestCase): with self.assertRaisesMessage(ValueError, msg): editor.add_index(Author, index) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_invalid_topmost_expressions(self): - index = Index(Upper(F('name').desc()), name='name') + index = Index(Upper(F("name").desc()), name="name") msg = ( - '%s must be topmost expressions in an indexed expression.' + "%s must be topmost expressions in an indexed expression." % self._index_expressions_wrappers() ) with connection.schema_editor() as editor: with self.assertRaisesMessage(ValueError, msg): editor.add_index(Author, index) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index(self): with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(Lower('name').desc(), name='func_lower_idx') + index = Index(Lower("name").desc(), name="func_lower_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Author, index) sql = index.create_sql(Author, editor) table = Author._meta.db_table if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, index.name, ['DESC']) + self.assertIndexOrder(table, index.name, ["DESC"]) # SQL contains a database function. - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIn('LOWER(%s)' % editor.quote_name('name'), str(sql)) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIn("LOWER(%s)" % editor.quote_name("name"), str(sql)) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_f(self): with connection.schema_editor() as editor: editor.create_model(Tag) - index = Index('slug', F('title').desc(), name='func_f_idx') + index = Index("slug", F("title").desc(), name="func_f_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Tag, index) @@ -2886,24 +3190,24 @@ class SchemaTests(TransactionTestCase): table = Tag._meta.db_table self.assertIn(index.name, self.get_constraints(table)) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(Tag._meta.db_table, index.name, ['ASC', 'DESC']) + self.assertIndexOrder(Tag._meta.db_table, index.name, ["ASC", "DESC"]) # SQL contains columns. - self.assertIs(sql.references_column(table, 'slug'), True) - self.assertIs(sql.references_column(table, 'title'), True) + self.assertIs(sql.references_column(table, "slug"), True) + self.assertIs(sql.references_column(table, "title"), True) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Tag, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_lookups(self): with connection.schema_editor() as editor: editor.create_model(Author) with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs): index = Index( - F('name__lower'), - F('weight__abs'), - name='func_lower_abs_lookup_idx', + F("name__lower"), + F("weight__abs"), + name="func_lower_abs_lookup_idx", ) # Add index. with connection.schema_editor() as editor: @@ -2912,18 +3216,18 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table self.assertIn(index.name, self.get_constraints(table)) # SQL contains columns. - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIs(sql.references_column(table, 'weight'), True) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIs(sql.references_column(table, "weight"), True) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_composite_func_index(self): with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(Lower('name'), Upper('name'), name='func_lower_upper_idx') + index = Index(Lower("name"), Upper("name"), name="func_lower_upper_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Author, index) @@ -2931,26 +3235,26 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table self.assertIn(index.name, self.get_constraints(table)) # SQL contains database functions. - self.assertIs(sql.references_column(table, 'name'), True) + self.assertIs(sql.references_column(table, "name"), True) sql = str(sql) - self.assertIn('LOWER(%s)' % editor.quote_name('name'), sql) - self.assertIn('UPPER(%s)' % editor.quote_name('name'), sql) - self.assertLess(sql.index('LOWER'), sql.index('UPPER')) + self.assertIn("LOWER(%s)" % editor.quote_name("name"), sql) + self.assertIn("UPPER(%s)" % editor.quote_name("name"), sql) + self.assertLess(sql.index("LOWER"), sql.index("UPPER")) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_composite_func_index_field_and_expression(self): with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) index = Index( - F('author').desc(), - Lower('title').asc(), - 'pub_date', - name='func_f_lower_field_idx', + F("author").desc(), + Lower("title").asc(), + "pub_date", + name="func_f_lower_field_idx", ) # Add index. with connection.schema_editor() as editor: @@ -2959,76 +3263,74 @@ class SchemaTests(TransactionTestCase): table = Book._meta.db_table constraints = self.get_constraints(table) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, index.name, ['DESC', 'ASC', 'ASC']) - self.assertEqual(len(constraints[index.name]['columns']), 3) - self.assertEqual(constraints[index.name]['columns'][2], 'pub_date') + self.assertIndexOrder(table, index.name, ["DESC", "ASC", "ASC"]) + self.assertEqual(len(constraints[index.name]["columns"]), 3) + self.assertEqual(constraints[index.name]["columns"][2], "pub_date") # SQL contains database functions and columns. - self.assertIs(sql.references_column(table, 'author_id'), True) - self.assertIs(sql.references_column(table, 'title'), True) - self.assertIs(sql.references_column(table, 'pub_date'), True) - self.assertIn('LOWER(%s)' % editor.quote_name('title'), str(sql)) + self.assertIs(sql.references_column(table, "author_id"), True) + self.assertIs(sql.references_column(table, "title"), True) + self.assertIs(sql.references_column(table, "pub_date"), True) + self.assertIn("LOWER(%s)" % editor.quote_name("title"), str(sql)) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Book, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') - @isolate_apps('schema') + @skipUnlessDBFeature("supports_expression_indexes") + @isolate_apps("schema") def test_func_index_f_decimalfield(self): class Node(Model): value = DecimalField(max_digits=5, decimal_places=2) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Node) - index = Index(F('value'), name='func_f_decimalfield_idx') + index = Index(F("value"), name="func_f_decimalfield_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Node, index) sql = index.create_sql(Node, editor) table = Node._meta.db_table self.assertIn(index.name, self.get_constraints(table)) - self.assertIs(sql.references_column(table, 'value'), True) + self.assertIs(sql.references_column(table, "value"), True) # SQL doesn't contain casting. - self.assertNotIn('CAST', str(sql)) + self.assertNotIn("CAST", str(sql)) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Node, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_cast(self): with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(Cast('weight', FloatField()), name='func_cast_idx') + index = Index(Cast("weight", FloatField()), name="func_cast_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Author, index) sql = index.create_sql(Author, editor) table = Author._meta.db_table self.assertIn(index.name, self.get_constraints(table)) - self.assertIs(sql.references_column(table, 'weight'), True) + self.assertIs(sql.references_column(table, "weight"), True) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_collate(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest( - 'This backend does not support case-insensitive collations.' - ) + self.skipTest("This backend does not support case-insensitive collations.") with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(BookWithSlug) index = Index( - Collate(F('title'), collation=collation).desc(), - Collate('slug', collation=collation), - name='func_collate_idx', + Collate(F("title"), collation=collation).desc(), + Collate("slug", collation=collation), + name="func_collate_idx", ) # Add index. with connection.schema_editor() as editor: @@ -3037,29 +3339,27 @@ class SchemaTests(TransactionTestCase): table = Book._meta.db_table self.assertIn(index.name, self.get_constraints(table)) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, index.name, ['DESC', 'ASC']) + self.assertIndexOrder(table, index.name, ["DESC", "ASC"]) # SQL contains columns and a collation. - self.assertIs(sql.references_column(table, 'title'), True) - self.assertIs(sql.references_column(table, 'slug'), True) - self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql)) + self.assertIs(sql.references_column(table, "title"), True) + self.assertIs(sql.references_column(table, "slug"), True) + self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql)) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Book, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') - @skipIfDBFeature('collate_as_index_expression') + @skipUnlessDBFeature("supports_expression_indexes") + @skipIfDBFeature("collate_as_index_expression") def test_func_index_collate_f_ordered(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest( - 'This backend does not support case-insensitive collations.' - ) + self.skipTest("This backend does not support case-insensitive collations.") with connection.schema_editor() as editor: editor.create_model(Author) index = Index( - Collate(F('name').desc(), collation=collation), - name='func_collate_f_desc_idx', + Collate(F("name").desc(), collation=collation), + name="func_collate_f_desc_idx", ) # Add index. with connection.schema_editor() as editor: @@ -3068,20 +3368,20 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table self.assertIn(index.name, self.get_constraints(table)) if connection.features.supports_index_column_ordering: - self.assertIndexOrder(table, index.name, ['DESC']) + self.assertIndexOrder(table, index.name, ["DESC"]) # SQL contains columns and a collation. - self.assertIs(sql.references_column(table, 'name'), True) - self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql)) + self.assertIs(sql.references_column(table, "name"), True) + self.assertIn("COLLATE %s" % editor.quote_name(collation), str(sql)) # Remove index. with connection.schema_editor() as editor: editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_calc(self): with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(F('height') / (F('weight') + Value(5)), name='func_calc_idx') + index = Index(F("height") / (F("weight") + Value(5)), name="func_calc_idx") # Add index. with connection.schema_editor() as editor: editor.add_index(Author, index) @@ -3089,15 +3389,15 @@ class SchemaTests(TransactionTestCase): table = Author._meta.db_table self.assertIn(index.name, self.get_constraints(table)) # SQL contains columns and expressions. - self.assertIs(sql.references_column(table, 'height'), True) - self.assertIs(sql.references_column(table, 'weight'), True) + self.assertIs(sql.references_column(table, "height"), True) + self.assertIs(sql.references_column(table, "weight"), True) sql = str(sql) self.assertIs( - sql.index(editor.quote_name('height')) < - sql.index('/') < - sql.index(editor.quote_name('weight')) < - sql.index('+') < - sql.index('5'), + sql.index(editor.quote_name("height")) + < sql.index("/") + < sql.index(editor.quote_name("weight")) + < sql.index("+") + < sql.index("5"), True, ) # Remove index. @@ -3105,69 +3405,69 @@ class SchemaTests(TransactionTestCase): editor.remove_index(Author, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes', 'supports_json_field') - @isolate_apps('schema') + @skipUnlessDBFeature("supports_expression_indexes", "supports_json_field") + @isolate_apps("schema") def test_func_index_json_key_transform(self): class JSONModel(Model): field = JSONField() class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(JSONModel) self.isolated_local_models = [JSONModel] - index = Index('field__some_key', name='func_json_key_idx') + index = Index("field__some_key", name="func_json_key_idx") with connection.schema_editor() as editor: editor.add_index(JSONModel, index) sql = index.create_sql(JSONModel, editor) table = JSONModel._meta.db_table self.assertIn(index.name, self.get_constraints(table)) - self.assertIs(sql.references_column(table, 'field'), True) + self.assertIs(sql.references_column(table, "field"), True) with connection.schema_editor() as editor: editor.remove_index(JSONModel, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipUnlessDBFeature('supports_expression_indexes', 'supports_json_field') - @isolate_apps('schema') + @skipUnlessDBFeature("supports_expression_indexes", "supports_json_field") + @isolate_apps("schema") def test_func_index_json_key_transform_cast(self): class JSONModel(Model): field = JSONField() class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(JSONModel) self.isolated_local_models = [JSONModel] index = Index( - Cast(KeyTextTransform('some_key', 'field'), IntegerField()), - name='func_json_key_cast_idx', + Cast(KeyTextTransform("some_key", "field"), IntegerField()), + name="func_json_key_cast_idx", ) with connection.schema_editor() as editor: editor.add_index(JSONModel, index) sql = index.create_sql(JSONModel, editor) table = JSONModel._meta.db_table self.assertIn(index.name, self.get_constraints(table)) - self.assertIs(sql.references_column(table, 'field'), True) + self.assertIs(sql.references_column(table, "field"), True) with connection.schema_editor() as editor: editor.remove_index(JSONModel, index) self.assertNotIn(index.name, self.get_constraints(table)) - @skipIfDBFeature('supports_expression_indexes') + @skipIfDBFeature("supports_expression_indexes") def test_func_index_unsupported(self): # Index is ignored on databases that don't support indexes on # expressions. with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(F('name'), name='random_idx') + index = Index(F("name"), name="random_idx") with connection.schema_editor() as editor, self.assertNumQueries(0): self.assertIsNone(editor.add_index(Author, index)) self.assertIsNone(editor.remove_index(Author, index)) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_nonexistent_field(self): - index = Index(Lower('nonexistent'), name='func_nonexistent_idx') + index = Index(Lower("nonexistent"), name="func_nonexistent_idx") msg = ( "Cannot resolve keyword 'nonexistent' into field. Choices are: " "height, id, name, uuid, weight" @@ -3176,11 +3476,11 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.add_index(Author, index) - @skipUnlessDBFeature('supports_expression_indexes') + @skipUnlessDBFeature("supports_expression_indexes") def test_func_index_nondeterministic(self): with connection.schema_editor() as editor: editor.create_model(Author) - index = Index(Random(), name='func_random_idx') + index = Index(Random(), name="func_random_idx") with connection.schema_editor() as editor: with self.assertRaises(DatabaseError): editor.add_index(Author, index) @@ -3193,7 +3493,7 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Tag) # Ensure the table is there and has the right PK - self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'id') + self.assertEqual(self.get_primary_key(Tag._meta.db_table), "id") # Alter to change the PK id_field = Tag._meta.get_field("id") old_field = Tag._meta.get_field("slug") @@ -3205,25 +3505,27 @@ class SchemaTests(TransactionTestCase): editor.alter_field(Tag, old_field, new_field) # Ensure the PK changed self.assertNotIn( - 'id', + "id", self.get_indexes(Tag._meta.db_table), ) - self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'slug') + self.assertEqual(self.get_primary_key(Tag._meta.db_table), "slug") def test_context_manager_exit(self): """ Ensures transaction is correctly closed when an error occurs inside a SchemaEditor context. """ + class SomeError(Exception): pass + try: with connection.schema_editor(): raise SomeError except SomeError: self.assertFalse(connection.in_atomic_block) - @skipIfDBFeature('can_rollback_ddl') + @skipIfDBFeature("can_rollback_ddl") def test_unsupported_transactional_ddl_disallowed(self): message = ( "Executing DDL statements while in a transaction on databases " @@ -3231,9 +3533,11 @@ class SchemaTests(TransactionTestCase): ) with atomic(), connection.schema_editor() as editor: with self.assertRaisesMessage(TransactionManagementError, message): - editor.execute(editor.sql_create_table % {'table': 'foo', 'definition': ''}) + editor.execute( + editor.sql_create_table % {"table": "foo", "definition": ""} + ) - @skipUnlessDBFeature('supports_foreign_keys', 'indexes_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys", "indexes_foreign_keys") def test_foreign_key_index_long_names_regression(self): """ Regression test for #21497. @@ -3244,7 +3548,9 @@ class SchemaTests(TransactionTestCase): editor.create_model(AuthorWithEvenLongerName) editor.create_model(BookWithLongName) # Find the properly shortened column name - column_name = connection.ops.quote_name("author_foreign_key_with_really_long_field_name_id") + column_name = connection.ops.quote_name( + "author_foreign_key_with_really_long_field_name_id" + ) column_name = column_name[1:-1].lower() # unquote, and, for Oracle, un-upcase # Ensure the table is there and has an index on the column self.assertIn( @@ -3252,7 +3558,7 @@ class SchemaTests(TransactionTestCase): self.get_indexes(BookWithLongName._meta.db_table), ) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_add_foreign_key_long_names(self): """ Regression test for #23009. @@ -3263,39 +3569,47 @@ class SchemaTests(TransactionTestCase): editor.create_model(AuthorWithEvenLongerName) editor.create_model(BookWithLongName) # Add a second FK, this would fail due to long ref name before the fix - new_field = ForeignKey(AuthorWithEvenLongerName, CASCADE, related_name="something") - new_field.set_attributes_from_name("author_other_really_long_named_i_mean_so_long_fk") + new_field = ForeignKey( + AuthorWithEvenLongerName, CASCADE, related_name="something" + ) + new_field.set_attributes_from_name( + "author_other_really_long_named_i_mean_so_long_fk" + ) with connection.schema_editor() as editor: editor.add_field(BookWithLongName, new_field) - @isolate_apps('schema') - @skipUnlessDBFeature('supports_foreign_keys') + @isolate_apps("schema") + @skipUnlessDBFeature("supports_foreign_keys") def test_add_foreign_key_quoted_db_table(self): class Author(Model): class Meta: db_table = '"table_author_double_quoted"' - app_label = 'schema' + app_label = "schema" class Book(Model): author = ForeignKey(Author, CASCADE) class Meta: - app_label = 'schema' + app_label = "schema" with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) - if connection.vendor == 'mysql': - self.assertForeignKeyExists(Book, 'author_id', '"table_author_double_quoted"') + if connection.vendor == "mysql": + self.assertForeignKeyExists( + Book, "author_id", '"table_author_double_quoted"' + ) else: - self.assertForeignKeyExists(Book, 'author_id', 'table_author_double_quoted') + self.assertForeignKeyExists(Book, "author_id", "table_author_double_quoted") def test_add_foreign_object(self): with connection.schema_editor() as editor: editor.create_model(BookForeignObj) - new_field = ForeignObject(Author, on_delete=CASCADE, from_fields=['author_id'], to_fields=['id']) - new_field.set_attributes_from_name('author') + new_field = ForeignObject( + Author, on_delete=CASCADE, from_fields=["author_id"], to_fields=["id"] + ) + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.add_field(BookForeignObj, new_field) @@ -3309,8 +3623,10 @@ class SchemaTests(TransactionTestCase): try: editor.create_model(Thing) except OperationalError as e: - self.fail("Errors when applying initial migration for a model " - "with a table named after an SQL reserved word: %s" % e) + self.fail( + "Errors when applying initial migration for a model " + "with a table named after an SQL reserved word: %s" % e + ) # The table is there list(Thing.objects.all()) # Clean up that table @@ -3324,8 +3640,9 @@ class SchemaTests(TransactionTestCase): """ #23065 - Constraint names must be quoted if they contain capital letters. """ + def get_field(*args, field_class=IntegerField, **kwargs): - kwargs['db_column'] = "CamelCase" + kwargs["db_column"] = "CamelCase" field = field_class(*args, **kwargs) field.set_attributes_from_name("CamelCase") return field @@ -3340,10 +3657,11 @@ class SchemaTests(TransactionTestCase): editor.create_model(model) editor.add_field(model, field) - constraint_name = 'CamelCaseIndex' + constraint_name = "CamelCaseIndex" expected_constraint_name = identifier_converter(constraint_name) editor.execute( - editor.sql_create_index % { + editor.sql_create_index + % { "table": editor.quote_name(table), "name": editor.quote_name(constraint_name), "using": "", @@ -3353,22 +3671,31 @@ class SchemaTests(TransactionTestCase): "include": "", } ) - self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) + self.assertIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) editor.alter_field(model, get_field(db_index=True), field, strict=True) - self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) + self.assertNotIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) - constraint_name = 'CamelCaseUniqConstraint' + constraint_name = "CamelCaseUniqConstraint" expected_constraint_name = identifier_converter(constraint_name) editor.execute(editor._create_unique_sql(model, [field], constraint_name)) - self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) + self.assertIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) editor.alter_field(model, get_field(unique=True), field, strict=True) - self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) + self.assertNotIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) if editor.sql_create_fk: - constraint_name = 'CamelCaseFKConstraint' + constraint_name = "CamelCaseFKConstraint" expected_constraint_name = identifier_converter(constraint_name) editor.execute( - editor.sql_create_fk % { + editor.sql_create_fk + % { "table": editor.quote_name(table), "name": editor.quote_name(constraint_name), "column": editor.quote_name(column), @@ -3377,9 +3704,18 @@ class SchemaTests(TransactionTestCase): "deferrable": connection.ops.deferrable_sql(), } ) - self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) - editor.alter_field(model, get_field(Author, CASCADE, field_class=ForeignKey), field, strict=True) - self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table)) + self.assertIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) + editor.alter_field( + model, + get_field(Author, CASCADE, field_class=ForeignKey), + field, + strict=True, + ) + self.assertNotIn( + expected_constraint_name, self.get_constraints(model._meta.db_table) + ) def test_add_field_use_effective_default(self): """ @@ -3393,7 +3729,7 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(Author) self.assertNotIn("surname", columns) # Create a row - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") # Add new CharField to ensure default will be used from effective_default new_field = CharField(max_length=15, blank=True) new_field.set_attributes_from_name("surname") @@ -3403,7 +3739,10 @@ class SchemaTests(TransactionTestCase): with connection.cursor() as cursor: cursor.execute("SELECT surname FROM schema_author;") item = cursor.fetchall()[0] - self.assertEqual(item[0], None if connection.features.interprets_empty_strings_as_nulls else '') + self.assertEqual( + item[0], + None if connection.features.interprets_empty_strings_as_nulls else "", + ) def test_add_field_default_dropped(self): # Create the table @@ -3413,9 +3752,9 @@ class SchemaTests(TransactionTestCase): columns = self.column_classes(Author) self.assertNotIn("surname", columns) # Create a row - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") # Add new CharField with a default - new_field = CharField(max_length=15, blank=True, default='surname default') + new_field = CharField(max_length=15, blank=True, default="surname default") new_field.set_attributes_from_name("surname") with connection.schema_editor() as editor: editor.add_field(Author, new_field) @@ -3423,10 +3762,13 @@ class SchemaTests(TransactionTestCase): with connection.cursor() as cursor: cursor.execute("SELECT surname FROM schema_author;") item = cursor.fetchall()[0] - self.assertEqual(item[0], 'surname default') + self.assertEqual(item[0], "surname default") # And that the default is no longer set in the database. field = next( - f for f in connection.introspection.get_table_description(cursor, "schema_author") + f + for f in connection.introspection.get_table_description( + cursor, "schema_author" + ) if f.name == "surname" ) if connection.features.can_introspect_default: @@ -3436,74 +3778,77 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Add new nullable CharField with a default. - new_field = CharField(max_length=15, blank=True, null=True, default='surname') - new_field.set_attributes_from_name('surname') + new_field = CharField(max_length=15, blank=True, null=True, default="surname") + new_field.set_attributes_from_name("surname") with connection.schema_editor() as editor: editor.add_field(Author, new_field) - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") with connection.cursor() as cursor: - cursor.execute('SELECT surname FROM schema_author;') + cursor.execute("SELECT surname FROM schema_author;") item = cursor.fetchall()[0] self.assertIsNone(item[0]) field = next( f for f in connection.introspection.get_table_description( cursor, - 'schema_author', + "schema_author", ) - if f.name == 'surname' + if f.name == "surname" ) # Field is still nullable. self.assertTrue(field.null_ok) # The database default is no longer set. if connection.features.can_introspect_default: - self.assertIn(field.default, ['NULL', None]) + self.assertIn(field.default, ["NULL", None]) def test_add_textfield_default_nullable(self): with connection.schema_editor() as editor: editor.create_model(Author) # Add new nullable TextField with a default. - new_field = TextField(blank=True, null=True, default='text') - new_field.set_attributes_from_name('description') + new_field = TextField(blank=True, null=True, default="text") + new_field.set_attributes_from_name("description") with connection.schema_editor() as editor: editor.add_field(Author, new_field) - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") with connection.cursor() as cursor: - cursor.execute('SELECT description FROM schema_author;') + cursor.execute("SELECT description FROM schema_author;") item = cursor.fetchall()[0] self.assertIsNone(item[0]) field = next( f for f in connection.introspection.get_table_description( cursor, - 'schema_author', + "schema_author", ) - if f.name == 'description' + if f.name == "description" ) # Field is still nullable. self.assertTrue(field.null_ok) # The database default is no longer set. if connection.features.can_introspect_default: - self.assertIn(field.default, ['NULL', None]) + self.assertIn(field.default, ["NULL", None]) def test_alter_field_default_dropped(self): # Create the table with connection.schema_editor() as editor: editor.create_model(Author) # Create a row - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") self.assertIsNone(Author.objects.get().height) - old_field = Author._meta.get_field('height') + old_field = Author._meta.get_field("height") # The default from the new field is used in updating existing rows. new_field = IntegerField(blank=True, default=42) - new_field.set_attributes_from_name('height') + new_field.set_attributes_from_name("height") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) self.assertEqual(Author.objects.get().height, 42) # The database default should be removed. with connection.cursor() as cursor: field = next( - f for f in connection.introspection.get_table_description(cursor, "schema_author") + f + for f in connection.introspection.get_table_description( + cursor, "schema_author" + ) if f.name == "height" ) if connection.features.can_introspect_default: @@ -3516,14 +3861,16 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(AuthorWithDefaultHeight) - old_field = AuthorWithDefaultHeight._meta.get_field('height') + old_field = AuthorWithDefaultHeight._meta.get_field("height") new_default = old_field.default * 2 new_field = PositiveIntegerField(null=True, blank=True, default=new_default) - new_field.set_attributes_from_name('height') + new_field.set_attributes_from_name("height") with connection.schema_editor() as editor, self.assertNumQueries(0): - editor.alter_field(AuthorWithDefaultHeight, old_field, new_field, strict=True) + editor.alter_field( + AuthorWithDefaultHeight, old_field, new_field, strict=True + ) - @skipUnlessDBFeature('supports_foreign_keys') + @skipUnlessDBFeature("supports_foreign_keys") def test_alter_field_fk_attributes_noop(self): """ No queries are performed when changing field attributes that don't @@ -3532,21 +3879,21 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) - old_field = Book._meta.get_field('author') + old_field = Book._meta.get_field("author") new_field = ForeignKey( Author, blank=True, editable=False, - error_messages={'invalid': 'error message'}, - help_text='help text', - limit_choices_to={'limit': 'choice'}, + error_messages={"invalid": "error message"}, + help_text="help text", + limit_choices_to={"limit": "choice"}, on_delete=PROTECT, - related_name='related_name', - related_query_name='related_query_name', + related_name="related_name", + related_query_name="related_query_name", validators=[lambda x: x], - verbose_name='verbose name', + verbose_name="verbose name", ) - new_field.set_attributes_from_name('author') + new_field.set_attributes_from_name("author") with connection.schema_editor() as editor, self.assertNumQueries(0): editor.alter_field(Book, old_field, new_field, strict=True) with connection.schema_editor() as editor, self.assertNumQueries(0): @@ -3557,233 +3904,244 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.create_model(Author) # Create a row - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") # Create a field that has an unhashable default new_field = TextField(default={}) new_field.set_attributes_from_name("info") with connection.schema_editor() as editor: editor.add_field(Author, new_field) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_add_indexed_charfield(self): field = CharField(max_length=255, db_index=True) - field.set_attributes_from_name('nom_de_plume') + field.set_attributes_from_name("nom_de_plume") with connection.schema_editor() as editor: editor.create_model(Author) editor.add_field(Author, field) # Should create two indexes; one for like operator. self.assertEqual( - self.get_constraints_for_column(Author, 'nom_de_plume'), - ['schema_author_nom_de_plume_7570a851', 'schema_author_nom_de_plume_7570a851_like'], + self.get_constraints_for_column(Author, "nom_de_plume"), + [ + "schema_author_nom_de_plume_7570a851", + "schema_author_nom_de_plume_7570a851_like", + ], ) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_add_unique_charfield(self): field = CharField(max_length=255, unique=True) - field.set_attributes_from_name('nom_de_plume') + field.set_attributes_from_name("nom_de_plume") with connection.schema_editor() as editor: editor.create_model(Author) editor.add_field(Author, field) # Should create two indexes; one for like operator. self.assertEqual( - self.get_constraints_for_column(Author, 'nom_de_plume'), - ['schema_author_nom_de_plume_7570a851_like', 'schema_author_nom_de_plume_key'] + self.get_constraints_for_column(Author, "nom_de_plume"), + [ + "schema_author_nom_de_plume_7570a851_like", + "schema_author_nom_de_plume_key", + ], ) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_add_index_to_charfield(self): # Create the table and verify no initial indexes. with connection.schema_editor() as editor: editor.create_model(Author) - self.assertEqual(self.get_constraints_for_column(Author, 'name'), []) + self.assertEqual(self.get_constraints_for_column(Author, "name"), []) # Alter to add db_index=True and create 2 indexes. - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field = CharField(max_length=255, db_index=True) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(Author, 'name'), - ['schema_author_name_1fbc5617', 'schema_author_name_1fbc5617_like'] + self.get_constraints_for_column(Author, "name"), + ["schema_author_name_1fbc5617", "schema_author_name_1fbc5617_like"], ) # Remove db_index=True to drop both indexes. with connection.schema_editor() as editor: editor.alter_field(Author, new_field, old_field, strict=True) - self.assertEqual(self.get_constraints_for_column(Author, 'name'), []) + self.assertEqual(self.get_constraints_for_column(Author, "name"), []) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_add_unique_to_charfield(self): # Create the table and verify no initial indexes. with connection.schema_editor() as editor: editor.create_model(Author) - self.assertEqual(self.get_constraints_for_column(Author, 'name'), []) + self.assertEqual(self.get_constraints_for_column(Author, "name"), []) # Alter to add unique=True and create 2 indexes. - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field = CharField(max_length=255, unique=True) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(Author, 'name'), - ['schema_author_name_1fbc5617_like', 'schema_author_name_1fbc5617_uniq'] + self.get_constraints_for_column(Author, "name"), + ["schema_author_name_1fbc5617_like", "schema_author_name_1fbc5617_uniq"], ) # Remove unique=True to drop both indexes. with connection.schema_editor() as editor: editor.alter_field(Author, new_field, old_field, strict=True) - self.assertEqual(self.get_constraints_for_column(Author, 'name'), []) + self.assertEqual(self.get_constraints_for_column(Author, "name"), []) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_add_index_to_textfield(self): # Create the table and verify no initial indexes. with connection.schema_editor() as editor: editor.create_model(Note) - self.assertEqual(self.get_constraints_for_column(Note, 'info'), []) + self.assertEqual(self.get_constraints_for_column(Note, "info"), []) # Alter to add db_index=True and create 2 indexes. - old_field = Note._meta.get_field('info') + old_field = Note._meta.get_field("info") new_field = TextField(db_index=True) - new_field.set_attributes_from_name('info') + new_field.set_attributes_from_name("info") with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(Note, 'info'), - ['schema_note_info_4b0ea695', 'schema_note_info_4b0ea695_like'] + self.get_constraints_for_column(Note, "info"), + ["schema_note_info_4b0ea695", "schema_note_info_4b0ea695_like"], ) # Remove db_index=True to drop both indexes. with connection.schema_editor() as editor: editor.alter_field(Note, new_field, old_field, strict=True) - self.assertEqual(self.get_constraints_for_column(Note, 'info'), []) + self.assertEqual(self.get_constraints_for_column(Note, "info"), []) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_add_unique_to_charfield_with_db_index(self): # Create the table and verify initial indexes. with connection.schema_editor() as editor: editor.create_model(BookWithoutAuthor) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"], ) # Alter to add unique=True (should replace the index) - old_field = BookWithoutAuthor._meta.get_field('title') + old_field = BookWithoutAuthor._meta.get_field("title") new_field = CharField(max_length=100, db_index=True, unique=True) - new_field.set_attributes_from_name('title') + new_field.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"], ) # Alter to remove unique=True (should drop unique index) new_field2 = CharField(max_length=100, db_index=True) - new_field2.set_attributes_from_name('title') + new_field2.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"], ) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_remove_unique_and_db_index_from_charfield(self): # Create the table and verify initial indexes. with connection.schema_editor() as editor: editor.create_model(BookWithoutAuthor) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"], ) # Alter to add unique=True (should replace the index) - old_field = BookWithoutAuthor._meta.get_field('title') + old_field = BookWithoutAuthor._meta.get_field("title") new_field = CharField(max_length=100, db_index=True, unique=True) - new_field.set_attributes_from_name('title') + new_field.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"], ) # Alter to remove both unique=True and db_index=True (should drop all indexes) new_field2 = CharField(max_length=100) - new_field2.set_attributes_from_name('title') + new_field2.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True) - self.assertEqual(self.get_constraints_for_column(BookWithoutAuthor, 'title'), []) + self.assertEqual( + self.get_constraints_for_column(BookWithoutAuthor, "title"), [] + ) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_swap_unique_and_db_index_with_charfield(self): # Create the table and verify initial indexes. with connection.schema_editor() as editor: editor.create_model(BookWithoutAuthor) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"], ) # Alter to set unique=True and remove db_index=True (should replace the index) - old_field = BookWithoutAuthor._meta.get_field('title') + old_field = BookWithoutAuthor._meta.get_field("title") new_field = CharField(max_length=100, unique=True) - new_field.set_attributes_from_name('title') + new_field.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff_like", "schema_book_title_2dfb2dff_uniq"], ) # Alter to set db_index=True and remove unique=True (should restore index) new_field2 = CharField(max_length=100, db_index=True) - new_field2.set_attributes_from_name('title') + new_field2.set_attributes_from_name("title") with connection.schema_editor() as editor: editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True) self.assertEqual( - self.get_constraints_for_column(BookWithoutAuthor, 'title'), - ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like'] + self.get_constraints_for_column(BookWithoutAuthor, "title"), + ["schema_book_title_2dfb2dff", "schema_book_title_2dfb2dff_like"], ) - @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific") + @unittest.skipUnless(connection.vendor == "postgresql", "PostgreSQL specific") def test_alter_field_add_db_index_to_charfield_with_unique(self): # Create the table and verify initial indexes. with connection.schema_editor() as editor: editor.create_model(Tag) self.assertEqual( - self.get_constraints_for_column(Tag, 'slug'), - ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key'] + self.get_constraints_for_column(Tag, "slug"), + ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"], ) # Alter to add db_index=True - old_field = Tag._meta.get_field('slug') + old_field = Tag._meta.get_field("slug") new_field = SlugField(db_index=True, unique=True) - new_field.set_attributes_from_name('slug') + new_field.set_attributes_from_name("slug") with connection.schema_editor() as editor: editor.alter_field(Tag, old_field, new_field, strict=True) self.assertEqual( - self.get_constraints_for_column(Tag, 'slug'), - ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key'] + self.get_constraints_for_column(Tag, "slug"), + ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"], ) # Alter to remove db_index=True new_field2 = SlugField(unique=True) - new_field2.set_attributes_from_name('slug') + new_field2.set_attributes_from_name("slug") with connection.schema_editor() as editor: editor.alter_field(Tag, new_field, new_field2, strict=True) self.assertEqual( - self.get_constraints_for_column(Tag, 'slug'), - ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key'] + self.get_constraints_for_column(Tag, "slug"), + ["schema_tag_slug_2c418ba3_like", "schema_tag_slug_key"], ) def test_alter_field_add_index_to_integerfield(self): # Create the table and verify no initial indexes. with connection.schema_editor() as editor: editor.create_model(Author) - self.assertEqual(self.get_constraints_for_column(Author, 'weight'), []) + self.assertEqual(self.get_constraints_for_column(Author, "weight"), []) # Alter to add db_index=True and create index. - old_field = Author._meta.get_field('weight') + old_field = Author._meta.get_field("weight") new_field = IntegerField(null=True, db_index=True) - new_field.set_attributes_from_name('weight') + new_field.set_attributes_from_name("weight") with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) - self.assertEqual(self.get_constraints_for_column(Author, 'weight'), ['schema_author_weight_587740f9']) + self.assertEqual( + self.get_constraints_for_column(Author, "weight"), + ["schema_author_weight_587740f9"], + ) # Remove db_index=True to drop index. with connection.schema_editor() as editor: editor.alter_field(Author, new_field, old_field, strict=True) - self.assertEqual(self.get_constraints_for_column(Author, 'weight'), []) + self.assertEqual(self.get_constraints_for_column(Author, "weight"), []) def test_alter_pk_with_self_referential_field(self): """ @@ -3792,22 +4150,26 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: editor.create_model(Node) - old_field = Node._meta.get_field('node_id') + old_field = Node._meta.get_field("node_id") new_field = AutoField(primary_key=True) - new_field.set_attributes_from_name('id') + new_field.set_attributes_from_name("id") with connection.schema_editor() as editor: editor.alter_field(Node, old_field, new_field, strict=True) - self.assertForeignKeyExists(Node, 'parent_id', Node._meta.db_table) + self.assertForeignKeyExists(Node, "parent_id", Node._meta.db_table) - @mock.patch('django.db.backends.base.schema.datetime') - @mock.patch('django.db.backends.base.schema.timezone') - def test_add_datefield_and_datetimefield_use_effective_default(self, mocked_datetime, mocked_tz): + @mock.patch("django.db.backends.base.schema.datetime") + @mock.patch("django.db.backends.base.schema.timezone") + def test_add_datefield_and_datetimefield_use_effective_default( + self, mocked_datetime, mocked_tz + ): """ effective_default() should be used for DateField, DateTimeField, and TimeField if auto_now or auto_now_add is set (#25005). """ now = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1) - now_tz = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1, tzinfo=timezone.utc) + now_tz = datetime.datetime( + month=1, day=1, year=2000, hour=1, minute=1, tzinfo=timezone.utc + ) mocked_datetime.now = mock.MagicMock(return_value=now) mocked_tz.now = mock.MagicMock(return_value=now_tz) # Create the table @@ -3822,40 +4184,64 @@ class SchemaTests(TransactionTestCase): self.assertNotIn("tob_auto_now", columns) self.assertNotIn("tob_auto_now_add", columns) # Create a row - Author.objects.create(name='Anonymous1') + Author.objects.create(name="Anonymous1") # Ensure fields were added with the correct defaults dob_auto_now = DateField(auto_now=True) - dob_auto_now.set_attributes_from_name('dob_auto_now') + dob_auto_now.set_attributes_from_name("dob_auto_now") self.check_added_field_default( - editor, Author, dob_auto_now, 'dob_auto_now', now.date(), + editor, + Author, + dob_auto_now, + "dob_auto_now", + now.date(), cast_function=lambda x: x.date(), ) dob_auto_now_add = DateField(auto_now_add=True) - dob_auto_now_add.set_attributes_from_name('dob_auto_now_add') + dob_auto_now_add.set_attributes_from_name("dob_auto_now_add") self.check_added_field_default( - editor, Author, dob_auto_now_add, 'dob_auto_now_add', now.date(), + editor, + Author, + dob_auto_now_add, + "dob_auto_now_add", + now.date(), cast_function=lambda x: x.date(), ) dtob_auto_now = DateTimeField(auto_now=True) - dtob_auto_now.set_attributes_from_name('dtob_auto_now') + dtob_auto_now.set_attributes_from_name("dtob_auto_now") self.check_added_field_default( - editor, Author, dtob_auto_now, 'dtob_auto_now', now, + editor, + Author, + dtob_auto_now, + "dtob_auto_now", + now, ) dt_tm_of_birth_auto_now_add = DateTimeField(auto_now_add=True) - dt_tm_of_birth_auto_now_add.set_attributes_from_name('dtob_auto_now_add') + dt_tm_of_birth_auto_now_add.set_attributes_from_name("dtob_auto_now_add") self.check_added_field_default( - editor, Author, dt_tm_of_birth_auto_now_add, 'dtob_auto_now_add', now, + editor, + Author, + dt_tm_of_birth_auto_now_add, + "dtob_auto_now_add", + now, ) tob_auto_now = TimeField(auto_now=True) - tob_auto_now.set_attributes_from_name('tob_auto_now') + tob_auto_now.set_attributes_from_name("tob_auto_now") self.check_added_field_default( - editor, Author, tob_auto_now, 'tob_auto_now', now.time(), + editor, + Author, + tob_auto_now, + "tob_auto_now", + now.time(), cast_function=lambda x: x.time(), ) tob_auto_now_add = TimeField(auto_now_add=True) - tob_auto_now_add.set_attributes_from_name('tob_auto_now_add') + tob_auto_now_add.set_attributes_from_name("tob_auto_now_add") self.check_added_field_default( - editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(), + editor, + Author, + tob_auto_now_add, + "tob_auto_now_add", + now.time(), cast_function=lambda x: x.time(), ) @@ -3866,15 +4252,17 @@ class SchemaTests(TransactionTestCase): """ with connection.schema_editor() as editor: max_name_length = connection.ops.max_name_length() or 200 - namespace = 'n' * max_name_length - table_name = 't' * max_name_length + namespace = "n" * max_name_length + table_name = "t" * max_name_length namespaced_table_name = '"%s"."%s"' % (namespace, table_name) self.assertEqual( editor._create_index_name(table_name, []), editor._create_index_name(namespaced_table_name, []), ) - @unittest.skipUnless(connection.vendor == 'oracle', 'Oracle specific db_table syntax') + @unittest.skipUnless( + connection.vendor == "oracle", "Oracle specific db_table syntax" + ) def test_creation_with_db_table_double_quotes(self): oracle_user = connection.creation._test_database_user() @@ -3882,7 +4270,7 @@ class SchemaTests(TransactionTestCase): name = CharField(max_length=30) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps db_table = '"%s"."DJANGO_STUDENT_TABLE"' % oracle_user @@ -3891,7 +4279,7 @@ class SchemaTests(TransactionTestCase): students = ManyToManyField(Student) class Meta: - app_label = 'schema' + app_label = "schema" apps = new_apps db_table = '"%s"."DJANGO_DOCUMENT_TABLE"' % oracle_user @@ -3901,33 +4289,35 @@ class SchemaTests(TransactionTestCase): editor.create_model(Student) editor.create_model(Document) - doc = Document.objects.create(name='Test Name') - student = Student.objects.create(name='Some man') + doc = Document.objects.create(name="Test Name") + student = Student.objects.create(name="Some man") doc.students.add(student) - @isolate_apps('schema') - @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific db_table syntax.') + @isolate_apps("schema") + @unittest.skipUnless( + connection.vendor == "postgresql", "PostgreSQL specific db_table syntax." + ) def test_namespaced_db_table_foreign_key_reference(self): with connection.cursor() as cursor: - cursor.execute('CREATE SCHEMA django_schema_tests') + cursor.execute("CREATE SCHEMA django_schema_tests") def delete_schema(): with connection.cursor() as cursor: - cursor.execute('DROP SCHEMA django_schema_tests CASCADE') + cursor.execute("DROP SCHEMA django_schema_tests CASCADE") self.addCleanup(delete_schema) class Author(Model): class Meta: - app_label = 'schema' + app_label = "schema" class Book(Model): class Meta: - app_label = 'schema' + app_label = "schema" db_table = '"django_schema_tests"."schema_book"' author = ForeignKey(Author, CASCADE) - author.set_attributes_from_name('author') + author.set_attributes_from_name("author") with connection.schema_editor() as editor: editor.create_model(Author) @@ -3939,51 +4329,52 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor(atomic=atomic_rename) as editor: editor.create_model(Author) editor.create_model(Book) - editor.alter_db_table(Author, 'schema_author', 'schema_renamed_author') - editor.alter_db_table(Author, 'schema_book', 'schema_renamed_book') + editor.alter_db_table(Author, "schema_author", "schema_renamed_author") + editor.alter_db_table(Author, "schema_book", "schema_renamed_book") try: self.assertGreater(len(editor.deferred_sql), 0) for statement in editor.deferred_sql: - self.assertIs(statement.references_table('schema_author'), False) - self.assertIs(statement.references_table('schema_book'), False) + self.assertIs(statement.references_table("schema_author"), False) + self.assertIs(statement.references_table("schema_book"), False) finally: - editor.alter_db_table(Author, 'schema_renamed_author', 'schema_author') - editor.alter_db_table(Author, 'schema_renamed_book', 'schema_book') + editor.alter_db_table(Author, "schema_renamed_author", "schema_author") + editor.alter_db_table(Author, "schema_renamed_book", "schema_book") def test_rename_column_renames_deferred_sql_references(self): with connection.schema_editor() as editor: editor.create_model(Author) editor.create_model(Book) - old_title = Book._meta.get_field('title') + old_title = Book._meta.get_field("title") new_title = CharField(max_length=100, db_index=True) - new_title.set_attributes_from_name('renamed_title') + new_title.set_attributes_from_name("renamed_title") editor.alter_field(Book, old_title, new_title) - old_author = Book._meta.get_field('author') + old_author = Book._meta.get_field("author") new_author = ForeignKey(Author, CASCADE) - new_author.set_attributes_from_name('renamed_author') + new_author.set_attributes_from_name("renamed_author") editor.alter_field(Book, old_author, new_author) self.assertGreater(len(editor.deferred_sql), 0) for statement in editor.deferred_sql: - self.assertIs(statement.references_column('book', 'title'), False) - self.assertIs(statement.references_column('book', 'author_id'), False) + self.assertIs(statement.references_column("book", "title"), False) + self.assertIs(statement.references_column("book", "author_id"), False) - @isolate_apps('schema') + @isolate_apps("schema") def test_referenced_field_without_constraint_rename_inside_atomic_block(self): """ Foreign keys without database level constraint don't prevent the field they reference from being renamed in an atomic block. """ + class Foo(Model): field = CharField(max_length=255, unique=True) class Meta: - app_label = 'schema' + app_label = "schema" class Bar(Model): - foo = ForeignKey(Foo, CASCADE, to_field='field', db_constraint=False) + foo = ForeignKey(Foo, CASCADE, to_field="field", db_constraint=False) class Meta: - app_label = 'schema' + app_label = "schema" self.isolated_local_models = [Foo, Bar] with connection.schema_editor() as editor: @@ -3991,27 +4382,28 @@ class SchemaTests(TransactionTestCase): editor.create_model(Bar) new_field = CharField(max_length=255, unique=True) - new_field.set_attributes_from_name('renamed') + new_field.set_attributes_from_name("renamed") with connection.schema_editor(atomic=True) as editor: - editor.alter_field(Foo, Foo._meta.get_field('field'), new_field) + editor.alter_field(Foo, Foo._meta.get_field("field"), new_field) - @isolate_apps('schema') + @isolate_apps("schema") def test_referenced_table_without_constraint_rename_inside_atomic_block(self): """ Foreign keys without database level constraint don't prevent the table they reference from being renamed in an atomic block. """ + class Foo(Model): field = CharField(max_length=255, unique=True) class Meta: - app_label = 'schema' + app_label = "schema" class Bar(Model): - foo = ForeignKey(Foo, CASCADE, to_field='field', db_constraint=False) + foo = ForeignKey(Foo, CASCADE, to_field="field", db_constraint=False) class Meta: - app_label = 'schema' + app_label = "schema" self.isolated_local_models = [Foo, Bar] with connection.schema_editor() as editor: @@ -4019,164 +4411,164 @@ class SchemaTests(TransactionTestCase): editor.create_model(Bar) new_field = CharField(max_length=255, unique=True) - new_field.set_attributes_from_name('renamed') + new_field.set_attributes_from_name("renamed") with connection.schema_editor(atomic=True) as editor: - editor.alter_db_table(Foo, Foo._meta.db_table, 'renamed_table') - Foo._meta.db_table = 'renamed_table' + editor.alter_db_table(Foo, Foo._meta.db_table, "renamed_table") + Foo._meta.db_table = "renamed_table" - @isolate_apps('schema') - @skipUnlessDBFeature('supports_collation_on_charfield') + @isolate_apps("schema") + @skipUnlessDBFeature("supports_collation_on_charfield") def test_db_collation_charfield(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest('Language collations are not supported.') + self.skipTest("Language collations are not supported.") class Foo(Model): field = CharField(max_length=255, db_collation=collation) class Meta: - app_label = 'schema' + app_label = "schema" self.isolated_local_models = [Foo] with connection.schema_editor() as editor: editor.create_model(Foo) self.assertEqual( - self.get_column_collation(Foo._meta.db_table, 'field'), + self.get_column_collation(Foo._meta.db_table, "field"), collation, ) - @isolate_apps('schema') - @skipUnlessDBFeature('supports_collation_on_textfield') + @isolate_apps("schema") + @skipUnlessDBFeature("supports_collation_on_textfield") def test_db_collation_textfield(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest('Language collations are not supported.') + self.skipTest("Language collations are not supported.") class Foo(Model): field = TextField(db_collation=collation) class Meta: - app_label = 'schema' + app_label = "schema" self.isolated_local_models = [Foo] with connection.schema_editor() as editor: editor.create_model(Foo) self.assertEqual( - self.get_column_collation(Foo._meta.db_table, 'field'), + self.get_column_collation(Foo._meta.db_table, "field"), collation, ) - @skipUnlessDBFeature('supports_collation_on_charfield') + @skipUnlessDBFeature("supports_collation_on_charfield") def test_add_field_db_collation(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest('Language collations are not supported.') + self.skipTest("Language collations are not supported.") with connection.schema_editor() as editor: editor.create_model(Author) new_field = CharField(max_length=255, db_collation=collation) - new_field.set_attributes_from_name('alias') + new_field.set_attributes_from_name("alias") with connection.schema_editor() as editor: editor.add_field(Author, new_field) columns = self.column_classes(Author) self.assertEqual( - columns['alias'][0], - connection.features.introspected_field_types['CharField'], + columns["alias"][0], + connection.features.introspected_field_types["CharField"], ) - self.assertEqual(columns['alias'][1][8], collation) + self.assertEqual(columns["alias"][1][8], collation) - @skipUnlessDBFeature('supports_collation_on_charfield') + @skipUnlessDBFeature("supports_collation_on_charfield") def test_alter_field_db_collation(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest('Language collations are not supported.') + self.skipTest("Language collations are not supported.") with connection.schema_editor() as editor: editor.create_model(Author) - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field = CharField(max_length=255, db_collation=collation) - new_field.set_attributes_from_name('name') + new_field.set_attributes_from_name("name") new_field.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) self.assertEqual( - self.get_column_collation(Author._meta.db_table, 'name'), + self.get_column_collation(Author._meta.db_table, "name"), collation, ) with connection.schema_editor() as editor: editor.alter_field(Author, new_field, old_field, strict=True) - self.assertIsNone(self.get_column_collation(Author._meta.db_table, 'name')) + self.assertIsNone(self.get_column_collation(Author._meta.db_table, "name")) - @skipUnlessDBFeature('supports_collation_on_charfield') + @skipUnlessDBFeature("supports_collation_on_charfield") def test_alter_field_type_and_db_collation(self): - collation = connection.features.test_collations.get('non_default') + collation = connection.features.test_collations.get("non_default") if not collation: - self.skipTest('Language collations are not supported.') + self.skipTest("Language collations are not supported.") with connection.schema_editor() as editor: editor.create_model(Note) - old_field = Note._meta.get_field('info') + old_field = Note._meta.get_field("info") new_field = CharField(max_length=255, db_collation=collation) - new_field.set_attributes_from_name('info') + new_field.set_attributes_from_name("info") new_field.model = Note with connection.schema_editor() as editor: editor.alter_field(Note, old_field, new_field, strict=True) columns = self.column_classes(Note) self.assertEqual( - columns['info'][0], - connection.features.introspected_field_types['CharField'], + columns["info"][0], + connection.features.introspected_field_types["CharField"], ) - self.assertEqual(columns['info'][1][8], collation) + self.assertEqual(columns["info"][1][8], collation) with connection.schema_editor() as editor: editor.alter_field(Note, new_field, old_field, strict=True) columns = self.column_classes(Note) - self.assertEqual(columns['info'][0], 'TextField') - self.assertIsNone(columns['info'][1][8]) + self.assertEqual(columns["info"][0], "TextField") + self.assertIsNone(columns["info"][1][8]) @skipUnlessDBFeature( - 'supports_collation_on_charfield', - 'supports_non_deterministic_collations', + "supports_collation_on_charfield", + "supports_non_deterministic_collations", ) def test_ci_cs_db_collation(self): - cs_collation = connection.features.test_collations.get('cs') - ci_collation = connection.features.test_collations.get('ci') + cs_collation = connection.features.test_collations.get("cs") + ci_collation = connection.features.test_collations.get("ci") try: - if connection.vendor == 'mysql': - cs_collation = 'latin1_general_cs' - elif connection.vendor == 'postgresql': - cs_collation = 'en-x-icu' + if connection.vendor == "mysql": + cs_collation = "latin1_general_cs" + elif connection.vendor == "postgresql": + cs_collation = "en-x-icu" with connection.cursor() as cursor: cursor.execute( "CREATE COLLATION IF NOT EXISTS case_insensitive " "(provider = icu, locale = 'und-u-ks-level2', " "deterministic = false)" ) - ci_collation = 'case_insensitive' + ci_collation = "case_insensitive" # Create the table. with connection.schema_editor() as editor: editor.create_model(Author) # Case-insensitive collation. - old_field = Author._meta.get_field('name') + old_field = Author._meta.get_field("name") new_field_ci = CharField(max_length=255, db_collation=ci_collation) - new_field_ci.set_attributes_from_name('name') + new_field_ci.set_attributes_from_name("name") new_field_ci.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field_ci, strict=True) - Author.objects.create(name='ANDREW') - self.assertIs(Author.objects.filter(name='Andrew').exists(), True) + Author.objects.create(name="ANDREW") + self.assertIs(Author.objects.filter(name="Andrew").exists(), True) # Case-sensitive collation. new_field_cs = CharField(max_length=255, db_collation=cs_collation) - new_field_cs.set_attributes_from_name('name') + new_field_cs.set_attributes_from_name("name") new_field_cs.model = Author with connection.schema_editor() as editor: editor.alter_field(Author, new_field_ci, new_field_cs, strict=True) - self.assertIs(Author.objects.filter(name='Andrew').exists(), False) + self.assertIs(Author.objects.filter(name="Andrew").exists(), False) finally: - if connection.vendor == 'postgresql': + if connection.vendor == "postgresql": with connection.cursor() as cursor: - cursor.execute('DROP COLLATION IF EXISTS case_insensitive') + cursor.execute("DROP COLLATION IF EXISTS case_insensitive") |
