mirror of
				https://github.com/django/django.git
				synced 2025-10-26 07:06:08 +00:00 
			
		
		
		
	Refs #28816 -- Prevented silencing data loss when decreasing CharField.max_length for ArrayField.base_field on PostgreSQL.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						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 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user