diff --git a/tests/schema/tests.py b/tests/schema/tests.py index ab8acbbb99..12c33a8433 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -22,7 +22,7 @@ from django.db.transaction import TransactionManagementError, atomic from django.test import ( TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, ) -from django.test.utils import CaptureQueriesContext, isolate_apps +from django.test.utils import CaptureQueriesContext, isolate_apps, patch_logger from django.utils import timezone from .fields import ( @@ -1545,6 +1545,42 @@ class SchemaTests(TransactionTestCase): TagUniqueRename.objects.create(title="bar", slug2="foo") Tag.objects.all().delete() + @isolate_apps('schema') + @unittest.skipIf(connection.vendor == 'sqlite', 'SQLite remakes the table on field alteration.') + 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' + + class Book(Model): + tags = ManyToManyField(Tag, related_name='books') + + class Meta: + app_label = 'schema' + + 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 patch_logger('django.db.backends.schema', 'debug') as logger_calls: + with connection.schema_editor() as editor: + editor.alter_field(Tag, Tag._meta.get_field('slug'), new_field) + # One SQL statement is executed to alter the field. + self.assertEqual(len(logger_calls), 1) + # Ensure that the field is still unique. + Tag.objects.create(title='foo', slug='foo') + with self.assertRaises(IntegrityError): + Tag.objects.create(title='bar', slug='foo') + def test_unique_together(self): """ Tests removing and adding unique_together constraints on a model.