mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #24817 -- Prevented loss of null info in MySQL field renaming.
This commit is contained in:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							300e8baf94
						
					
				
				
					commit
					80ad5472ce
				
			| @@ -542,12 +542,7 @@ class BaseDatabaseSchemaEditor(object): | |||||||
|                 self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name)) |                 self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name)) | ||||||
|         # Have they renamed the column? |         # Have they renamed the column? | ||||||
|         if old_field.column != new_field.column: |         if old_field.column != new_field.column: | ||||||
|             self.execute(self.sql_rename_column % { |             self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type)) | ||||||
|                 "table": self.quote_name(model._meta.db_table), |  | ||||||
|                 "old_column": self.quote_name(old_field.column), |  | ||||||
|                 "new_column": self.quote_name(new_field.column), |  | ||||||
|                 "type": new_type, |  | ||||||
|             }) |  | ||||||
|         # Next, start accumulating actions to do |         # Next, start accumulating actions to do | ||||||
|         actions = [] |         actions = [] | ||||||
|         null_actions = [] |         null_actions = [] | ||||||
| @@ -864,6 +859,14 @@ class BaseDatabaseSchemaEditor(object): | |||||||
|             output.append(self._create_index_sql(model, fields, suffix="_idx")) |             output.append(self._create_index_sql(model, fields, suffix="_idx")) | ||||||
|         return output |         return output | ||||||
|  |  | ||||||
|  |     def _rename_field_sql(self, table, old_field, new_field, new_type): | ||||||
|  |         return self.sql_rename_column % { | ||||||
|  |             "table": self.quote_name(table), | ||||||
|  |             "old_column": self.quote_name(old_field.column), | ||||||
|  |             "new_column": self.quote_name(new_field.column), | ||||||
|  |             "type": new_type, | ||||||
|  |         } | ||||||
|  |  | ||||||
|     def _create_fk_sql(self, model, field, suffix): |     def _create_fk_sql(self, model, field, suffix): | ||||||
|         from_table = model._meta.db_table |         from_table = model._meta.db_table | ||||||
|         from_column = field.column |         from_column = field.column | ||||||
|   | |||||||
| @@ -80,10 +80,21 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): | |||||||
|                 ) |                 ) | ||||||
|         return super(DatabaseSchemaEditor, self)._delete_composed_index(model, fields, *args) |         return super(DatabaseSchemaEditor, self)._delete_composed_index(model, fields, *args) | ||||||
|  |  | ||||||
|     def _alter_column_type_sql(self, table, old_field, new_field, new_type): |     def _set_field_new_type_null_status(self, field, new_type): | ||||||
|         # Keep null property of old field, if it has changed, it will be handled separately |         """ | ||||||
|         if old_field.null: |         Keep the null property of the old field. If it has changed, it will be | ||||||
|  |         handled separately. | ||||||
|  |         """ | ||||||
|  |         if field.null: | ||||||
|             new_type += " NULL" |             new_type += " NULL" | ||||||
|         else: |         else: | ||||||
|             new_type += " NOT NULL" |             new_type += " NOT NULL" | ||||||
|  |         return new_type | ||||||
|  |  | ||||||
|  |     def _alter_column_type_sql(self, table, old_field, new_field, new_type): | ||||||
|  |         new_type = self._set_field_new_type_null_status(old_field, new_type) | ||||||
|         return super(DatabaseSchemaEditor, self)._alter_column_type_sql(table, old_field, new_field, new_type) |         return super(DatabaseSchemaEditor, self)._alter_column_type_sql(table, old_field, new_field, new_type) | ||||||
|  |  | ||||||
|  |     def _rename_field_sql(self, table, old_field, new_field, new_type): | ||||||
|  |         new_type = self._set_field_new_type_null_status(old_field, new_type) | ||||||
|  |         return super(DatabaseSchemaEditor, self)._rename_field_sql(table, old_field, new_field, new_type) | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								docs/releases/1.7.9.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								docs/releases/1.7.9.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | ========================== | ||||||
|  | Django 1.7.9 release notes | ||||||
|  | ========================== | ||||||
|  |  | ||||||
|  | *Under development* | ||||||
|  |  | ||||||
|  | Django 1.7.9 fixes several bugs in 1.7.8. | ||||||
|  |  | ||||||
|  | * Prevented the loss of ``null``/``not null`` column properties during field | ||||||
|  |   renaming of MySQL databases (:ticket:`24817`). | ||||||
| @@ -25,3 +25,6 @@ Bugfixes | |||||||
|  |  | ||||||
| * Fixed a regression which caused template context processors to overwrite | * Fixed a regression which caused template context processors to overwrite | ||||||
|   variables set on a ``RequestContext`` after it's created (:ticket:`24847`). |   variables set on a ``RequestContext`` after it's created (:ticket:`24847`). | ||||||
|  |  | ||||||
|  | * Prevented the loss of ``null``/``not null`` column properties during field | ||||||
|  |   renaming of MySQL databases (:ticket:`24817`). | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ versions of the documentation contain the release notes for any later releases. | |||||||
| .. toctree:: | .. toctree:: | ||||||
|    :maxdepth: 1 |    :maxdepth: 1 | ||||||
|  |  | ||||||
|  |    1.7.9 | ||||||
|    1.7.8 |    1.7.8 | ||||||
|    1.7.7 |    1.7.7 | ||||||
|    1.7.6 |    1.7.6 | ||||||
|   | |||||||
| @@ -87,6 +87,14 @@ class Note(models.Model): | |||||||
|         apps = new_apps |         apps = new_apps | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NoteRename(models.Model): | ||||||
|  |     detail_info = models.TextField() | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         apps = new_apps | ||||||
|  |         db_table = "schema_note" | ||||||
|  |  | ||||||
|  |  | ||||||
| class Tag(models.Model): | class Tag(models.Model): | ||||||
|     title = models.CharField(max_length=255) |     title = models.CharField(max_length=255) | ||||||
|     slug = models.SlugField(unique=True) |     slug = models.SlugField(unique=True) | ||||||
|   | |||||||
| @@ -20,8 +20,8 @@ from django.test import TransactionTestCase, skipIfDBFeature | |||||||
| from .fields import CustomManyToManyField, InheritedManyToManyField | from .fields import CustomManyToManyField, InheritedManyToManyField | ||||||
| from .models import ( | from .models import ( | ||||||
|     Author, AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book, BookWeak, |     Author, AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book, BookWeak, | ||||||
|     BookWithLongName, BookWithO2O, BookWithSlug, Note, Tag, TagIndexed, |     BookWithLongName, BookWithO2O, BookWithSlug, Note, NoteRename, Tag, | ||||||
|     TagM2MTest, TagUniqueRename, Thing, UniqueTest, new_apps, |     TagIndexed, TagM2MTest, TagUniqueRename, Thing, UniqueTest, new_apps, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -751,6 +751,26 @@ class SchemaTests(TransactionTestCase): | |||||||
|         self.assertEqual(columns['display_name'][0], "CharField") |         self.assertEqual(columns['display_name'][0], "CharField") | ||||||
|         self.assertNotIn("name", columns) |         self.assertNotIn("name", columns) | ||||||
|  |  | ||||||
|  |     @skipIfDBFeature('interprets_empty_strings_as_nulls') | ||||||
|  |     def test_rename_keep_null_status(self): | ||||||
|  |         """ | ||||||
|  |         Renaming a field shouldn't affect the not null status. | ||||||
|  |         """ | ||||||
|  |         with connection.schema_editor() as editor: | ||||||
|  |             editor.create_model(Note) | ||||||
|  |         with self.assertRaises(IntegrityError): | ||||||
|  |             Note.objects.create(info=None) | ||||||
|  |         old_field = Note._meta.get_field("info") | ||||||
|  |         new_field = TextField() | ||||||
|  |         new_field.set_attributes_from_name("detail_info") | ||||||
|  |         with connection.schema_editor() as editor: | ||||||
|  |             editor.alter_field(Note, old_field, new_field, strict=True) | ||||||
|  |         columns = self.column_classes(Note) | ||||||
|  |         self.assertEqual(columns['detail_info'][0], "TextField") | ||||||
|  |         self.assertNotIn("info", columns) | ||||||
|  |         with self.assertRaises(IntegrityError): | ||||||
|  |             NoteRename.objects.create(detail_info=None) | ||||||
|  |  | ||||||
|     def _test_m2m_create(self, M2MFieldClass): |     def _test_m2m_create(self, M2MFieldClass): | ||||||
|         """ |         """ | ||||||
|         Tests M2M fields on models during creation |         Tests M2M fields on models during creation | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user