diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index cf18e7653e..a722e497c3 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -539,7 +539,7 @@ class BaseDatabaseSchemaEditor: fks_dropped.add((old_field.column,)) self.execute(self._delete_constraint_sql(self.sql_delete_fk, model, fk_name)) # Has unique been removed? - if old_field.unique and (not new_field.unique or (not old_field.primary_key and new_field.primary_key)): + if old_field.unique and (not new_field.unique or self._field_became_primary_key(old_field, new_field)): # Find the unique constraint for this field constraint_names = self._constraint_names(model, [old_field.column], unique=True, primary_key=False) if strict and len(constraint_names) != 1: @@ -689,9 +689,7 @@ class BaseDatabaseSchemaEditor: if old_field.primary_key and not new_field.primary_key: self._delete_primary_key(model, strict) # Added a unique? - if (not old_field.unique and new_field.unique) or ( - old_field.primary_key and not new_field.primary_key and new_field.unique - ): + if self._unique_should_be_added(old_field, new_field): self.execute(self._create_unique_sql(model, [new_field.column])) # Added an index? Add an index if db_index switched to True or a unique # constraint will no longer be used in lieu of an index. The following @@ -710,7 +708,7 @@ class BaseDatabaseSchemaEditor: if old_field.primary_key and new_field.primary_key and old_type != new_type: rels_to_update.extend(_related_non_m2m_objects(old_field, new_field)) # Changed to become primary key? - if not old_field.primary_key and new_field.primary_key: + if self._field_became_primary_key(old_field, new_field): # Make the new one self.execute( self.sql_create_pk % { @@ -966,6 +964,14 @@ class BaseDatabaseSchemaEditor: def _field_should_be_indexed(self, model, field): return field.db_index and not field.unique + def _field_became_primary_key(self, old_field, new_field): + return not old_field.primary_key and new_field.primary_key + + def _unique_should_be_added(self, old_field, new_field): + return (not old_field.unique and new_field.unique) or ( + old_field.primary_key and not new_field.primary_key and new_field.unique + ) + def _rename_field_sql(self, table, old_field, new_field, new_type): return self.sql_rename_column % { "table": self.quote_name(table), diff --git a/django/db/backends/oracle/schema.py b/django/db/backends/oracle/schema.py index 39c3b7e83d..105d6a4033 100644 --- a/django/db/backends/oracle/schema.py +++ b/django/db/backends/oracle/schema.py @@ -144,6 +144,12 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): return False return create_index + def _unique_should_be_added(self, old_field, new_field): + return ( + super()._unique_should_be_added(old_field, new_field) and + not self._field_became_primary_key(old_field, new_field) + ) + def _is_identity_column(self, table_name, column_name): with self.connection.cursor() as cursor: cursor.execute(""" diff --git a/tests/schema/models.py b/tests/schema/models.py index 4512d8bc01..f6bdbd8815 100644 --- a/tests/schema/models.py +++ b/tests/schema/models.py @@ -12,6 +12,7 @@ class Author(models.Model): name = models.CharField(max_length=255) height = models.PositiveIntegerField(null=True, blank=True) weight = models.IntegerField(null=True, blank=True) + uuid = models.UUIDField(null=True) class Meta: apps = new_apps diff --git a/tests/schema/tests.py b/tests/schema/tests.py index e1bf438ca3..f18b68d04d 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -12,7 +12,7 @@ from django.db.models.deletion import CASCADE, PROTECT from django.db.models.fields import ( AutoField, BigAutoField, BigIntegerField, BinaryField, BooleanField, CharField, DateField, DateTimeField, IntegerField, PositiveIntegerField, - SlugField, TextField, TimeField, + SlugField, TextField, TimeField, UUIDField, ) from django.db.models.fields.related import ( ForeignKey, ForeignObject, ManyToManyField, OneToOneField, @@ -603,6 +603,19 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) + def test_alter_not_unique_field_to_primary_key(self): + # Create the table. + with connection.schema_editor() as editor: + editor.create_model(Author) + # Change UUIDField to primary key. + old_field = Author._meta.get_field('uuid') + new_field = UUIDField(primary_key=True) + 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.alter_field(Author, old_field, new_field, strict=True) + def test_alter_text_field(self): # Regression for "BLOB/TEXT column 'info' can't have a default value") # on MySQL.