diff --git a/django/db/backends/postgresql/schema.py b/django/db/backends/postgresql/schema.py index 0738f009cd..82e91ce840 100644 --- a/django/db/backends/postgresql/schema.py +++ b/django/db/backends/postgresql/schema.py @@ -6,8 +6,6 @@ from django.db.backends.ddl_references import IndexColumns class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): - sql_alter_column_type = "ALTER COLUMN %(column)s TYPE %(type)s USING %(column)s::%(type)s" - sql_create_sequence = "CREATE SEQUENCE %(sequence)s" sql_delete_sequence = "DROP SEQUENCE IF EXISTS %(sequence)s CASCADE" sql_set_sequence_max = "SELECT setval('%(sequence)s', MAX(%(column)s)) FROM %(table)s" @@ -36,6 +34,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): output.append(like_index_statement) return output + def _field_data_type(self, field): + if field.is_relation: + return field.rel_db_type(self.connection) + return self.connection.data_types[field.get_internal_type()] + def _create_like_index_sql(self, model, field): """ Return the statement to create an index with varchar operator pattern @@ -59,7 +62,11 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): return None def _alter_column_type_sql(self, model, old_field, new_field, new_type): - """Make ALTER TYPE with SERIAL make sense.""" + self.sql_alter_column_type = 'ALTER COLUMN %(column)s TYPE %(type)s' + # Cast when data type changed. + if self._field_data_type(old_field) != self._field_data_type(new_field): + self.sql_alter_column_type += ' USING %(column)s::%(type)s' + # Make ALTER TYPE with SERIAL make sense. table = model._meta.db_table if new_type.lower() in ("serial", "bigserial"): column = new_field.column diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 0533b32859..64a1512a53 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -21,6 +21,7 @@ from django.db.models.fields.related import ( ) from django.db.models.indexes import Index from django.db.transaction import TransactionManagementError, atomic +from django.db.utils import DataError from django.test import ( TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature, ) @@ -803,6 +804,21 @@ class SchemaTests(TransactionTestCase): with connection.schema_editor() as editor: editor.alter_field(Author, old_field, new_field, strict=True) + @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) + # Change max_length of CharField. + old_field = Author._meta.get_field('name') + new_field = CharField(max_length=254) + new_field.set_attributes_from_name('name') + with connection.schema_editor() as editor: + msg = 'value too long for type character varying(254)' + with self.assertRaisesMessage(DataError, msg): + editor.alter_field(Author, old_field, new_field, strict=True) + def test_alter_textfield_to_null(self): """ #24307 - Should skip an alter statement on databases with