mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Fixed #33322 -- Fixed loss of assigned related object when saving relation with bulk_update().
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							9ac92b1efc
						
					
				
				
					commit
					ed2018037d
				
			| @@ -931,11 +931,13 @@ class Model(metaclass=ModelBase): | ||||
|             using=using, raw=raw, | ||||
|         ) | ||||
|  | ||||
|     def _prepare_related_fields_for_save(self, operation_name): | ||||
|     def _prepare_related_fields_for_save(self, operation_name, fields=None): | ||||
|         # Ensure that a model instance without a PK hasn't been assigned to | ||||
|         # a ForeignKey or OneToOneField on this model. If the field is | ||||
|         # nullable, allowing the save would result in silent data loss. | ||||
|         for field in self._meta.concrete_fields: | ||||
|             if fields and field not in fields: | ||||
|                 continue | ||||
|             # If the related field isn't cached, then an instance hasn't been | ||||
|             # assigned and there's no need to worry about this check. | ||||
|             if field.is_relation and field.is_cached(self): | ||||
|   | ||||
| @@ -549,6 +549,8 @@ class QuerySet: | ||||
|             raise ValueError('bulk_update() cannot be used with primary key fields.') | ||||
|         if not objs: | ||||
|             return 0 | ||||
|         for obj in objs: | ||||
|             obj._prepare_related_fields_for_save(operation_name='bulk_update', fields=fields) | ||||
|         # PK is used twice in the resulting update query, once in the filter | ||||
|         # and once in the WHEN. Each field will also have one CAST. | ||||
|         connection = connections[self.db] | ||||
|   | ||||
| @@ -7,7 +7,8 @@ from django.test import TestCase, skipUnlessDBFeature | ||||
|  | ||||
| from .models import ( | ||||
|     Article, CustomDbColumn, CustomPk, Detail, Individual, JSONFieldNullable, | ||||
|     Member, Note, Number, Order, Paragraph, SpecialCategory, Tag, Valid, | ||||
|     Member, Note, Number, Order, Paragraph, RelatedObject, SingleObject, | ||||
|     SpecialCategory, Tag, Valid, | ||||
| ) | ||||
|  | ||||
|  | ||||
| @@ -250,3 +251,32 @@ class BulkUpdateTests(TestCase): | ||||
|             obj.json_field = {'c': obj.json_field['a'] + 1} | ||||
|         JSONFieldNullable.objects.bulk_update(objs, ['json_field']) | ||||
|         self.assertCountEqual(JSONFieldNullable.objects.filter(json_field__has_key='c'), objs) | ||||
|  | ||||
|     def test_nullable_fk_after_related_save(self): | ||||
|         parent = RelatedObject.objects.create() | ||||
|         child = SingleObject() | ||||
|         parent.single = child | ||||
|         parent.single.save() | ||||
|         RelatedObject.objects.bulk_update([parent], fields=['single']) | ||||
|         self.assertEqual(parent.single_id, parent.single.pk) | ||||
|         parent.refresh_from_db() | ||||
|         self.assertEqual(parent.single, child) | ||||
|  | ||||
|     def test_unsaved_parent(self): | ||||
|         parent = RelatedObject.objects.create() | ||||
|         parent.single = SingleObject() | ||||
|         msg = ( | ||||
|             "bulk_update() prohibited to prevent data loss due to unsaved " | ||||
|             "related object 'single'." | ||||
|         ) | ||||
|         with self.assertRaisesMessage(ValueError, msg): | ||||
|             RelatedObject.objects.bulk_update([parent], fields=['single']) | ||||
|  | ||||
|     def test_unspecified_unsaved_parent(self): | ||||
|         parent = RelatedObject.objects.create() | ||||
|         parent.single = SingleObject() | ||||
|         parent.f = 42 | ||||
|         RelatedObject.objects.bulk_update([parent], fields=['f']) | ||||
|         parent.refresh_from_db() | ||||
|         self.assertEqual(parent.f, 42) | ||||
|         self.assertIsNone(parent.single) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user