Refs #28816 -- Prevented silencing data loss when decreasing CharField.max_length for ArrayField.base_field on PostgreSQL.
This commit is contained in:
parent
6f82df69ef
commit
ef4beafa2c
|
@ -47,6 +47,13 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
field.db_type(self.connection),
|
field.db_type(self.connection),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _field_base_data_types(self, field):
|
||||||
|
# Yield base data types for array fields.
|
||||||
|
if field.base_field.get_internal_type() == 'ArrayField':
|
||||||
|
yield from self._field_base_data_types(field.base_field)
|
||||||
|
else:
|
||||||
|
yield self._field_data_type(field.base_field)
|
||||||
|
|
||||||
def _create_like_index_sql(self, model, field):
|
def _create_like_index_sql(self, model, field):
|
||||||
"""
|
"""
|
||||||
Return the statement to create an index with varchar operator pattern
|
Return the statement to create an index with varchar operator pattern
|
||||||
|
@ -72,8 +79,15 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
def _alter_column_type_sql(self, model, old_field, new_field, new_type):
|
def _alter_column_type_sql(self, model, old_field, new_field, new_type):
|
||||||
self.sql_alter_column_type = 'ALTER COLUMN %(column)s TYPE %(type)s'
|
self.sql_alter_column_type = 'ALTER COLUMN %(column)s TYPE %(type)s'
|
||||||
# Cast when data type changed.
|
# Cast when data type changed.
|
||||||
if self._field_data_type(old_field) != self._field_data_type(new_field):
|
using_sql = ' USING %(column)s::%(type)s'
|
||||||
self.sql_alter_column_type += ' USING %(column)s::%(type)s'
|
new_internal_type = new_field.get_internal_type()
|
||||||
|
old_internal_type = old_field.get_internal_type()
|
||||||
|
if new_internal_type == 'ArrayField' and new_internal_type == old_internal_type:
|
||||||
|
# Compare base data types for array fields.
|
||||||
|
if list(self._field_base_data_types(old_field)) != list(self._field_base_data_types(new_field)):
|
||||||
|
self.sql_alter_column_type += using_sql
|
||||||
|
elif self._field_data_type(old_field) != self._field_data_type(new_field):
|
||||||
|
self.sql_alter_column_type += using_sql
|
||||||
# Make ALTER TYPE with SERIAL make sense.
|
# Make ALTER TYPE with SERIAL make sense.
|
||||||
table = strip_quotes(model._meta.db_table)
|
table = strip_quotes(model._meta.db_table)
|
||||||
serial_fields_map = {'bigserial': 'bigint', 'serial': 'integer', 'smallserial': 'smallint'}
|
serial_fields_map = {'bigserial': 'bigint', 'serial': 'integer', 'smallserial': 'smallint'}
|
||||||
|
|
|
@ -895,6 +895,54 @@ class SchemaTests(TransactionTestCase):
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
editor.alter_field(Foo, old_field, new_field, strict=True)
|
editor.alter_field(Foo, old_field, new_field, strict=True)
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
class ArrayModel(Model):
|
||||||
|
field = ArrayField(CharField(max_length=16))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
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')
|
||||||
|
new_field = ArrayField(CharField(max_length=15))
|
||||||
|
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)'
|
||||||
|
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')
|
||||||
|
def test_alter_array_field_decrease_nested_base_field_length(self):
|
||||||
|
from django.contrib.postgres.fields import ArrayField
|
||||||
|
|
||||||
|
class ArrayModel(Model):
|
||||||
|
field = ArrayField(ArrayField(CharField(max_length=16)))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
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')
|
||||||
|
new_field = ArrayField(ArrayField(CharField(max_length=15)))
|
||||||
|
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)'
|
||||||
|
with self.assertRaisesMessage(DataError, msg):
|
||||||
|
editor.alter_field(ArrayModel, old_field, new_field, strict=True)
|
||||||
|
|
||||||
def test_alter_textfield_to_null(self):
|
def test_alter_textfield_to_null(self):
|
||||||
"""
|
"""
|
||||||
#24307 - Should skip an alter statement on databases with
|
#24307 - Should skip an alter statement on databases with
|
||||||
|
|
Loading…
Reference in New Issue