mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Fixed #35044 -- Avoided clearing reverse relations and private fields when accessing deferred fields.
Regression ina7b5ad8b19for reverse relations and possibly in123b1d3fcffor private fields.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						parent
						
							74f7fe3f3d
						
					
				
				
					commit
					73df8b54a2
				
			| @@ -740,12 +740,16 @@ class Model(AltersData, metaclass=ModelBase): | ||||
|  | ||||
|         # Clear cached relations. | ||||
|         for field in self._meta.related_objects: | ||||
|             if field.is_cached(self): | ||||
|             if (fields is None or field.name in fields) and field.is_cached(self): | ||||
|                 field.delete_cached_value(self) | ||||
|  | ||||
|         # Clear cached private relations. | ||||
|         for field in self._meta.private_fields: | ||||
|             if field.is_relation and field.is_cached(self): | ||||
|             if ( | ||||
|                 (fields is None or field.name in fields) | ||||
|                 and field.is_relation | ||||
|                 and field.is_cached(self) | ||||
|             ): | ||||
|                 field.delete_cached_value(self) | ||||
|  | ||||
|         self._state.db = db_instance._state.db | ||||
|   | ||||
| @@ -45,6 +45,18 @@ class GenericForeignKeyTests(TestCase): | ||||
|         new_entity = answer.question | ||||
|         self.assertIsNot(old_entity, new_entity) | ||||
|  | ||||
|     def test_clear_cached_generic_relation_explicit_fields(self): | ||||
|         question = Question.objects.create(text="question") | ||||
|         answer = Answer.objects.create(text="answer", question=question) | ||||
|         old_question_obj = answer.question | ||||
|         # The reverse relation is not refreshed if not passed explicitly in | ||||
|         # `fields`. | ||||
|         answer.refresh_from_db(fields=["text"]) | ||||
|         self.assertIs(answer.question, old_question_obj) | ||||
|         answer.refresh_from_db(fields=["question"]) | ||||
|         self.assertIsNot(answer.question, old_question_obj) | ||||
|         self.assertEqual(answer.question, old_question_obj) | ||||
|  | ||||
|  | ||||
| class GenericRelationTests(TestCase): | ||||
|     def test_value_to_string(self): | ||||
| @@ -55,6 +67,29 @@ class GenericRelationTests(TestCase): | ||||
|         self.assertCountEqual(result, [answer1.pk, answer2.pk]) | ||||
|  | ||||
|  | ||||
| class DeferredGenericRelationTests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         cls.question = Question.objects.create(text="question") | ||||
|         cls.answer = Answer.objects.create(text="answer", question=cls.question) | ||||
|  | ||||
|     def test_defer_not_clear_cached_private_relations(self): | ||||
|         obj = Answer.objects.defer("text").get(pk=self.answer.pk) | ||||
|         with self.assertNumQueries(1): | ||||
|             obj.question | ||||
|         obj.text  # Accessing a deferred field. | ||||
|         with self.assertNumQueries(0): | ||||
|             obj.question | ||||
|  | ||||
|     def test_only_not_clear_cached_private_relations(self): | ||||
|         obj = Answer.objects.only("content_type", "object_id").get(pk=self.answer.pk) | ||||
|         with self.assertNumQueries(1): | ||||
|             obj.question | ||||
|         obj.text  # Accessing a deferred field. | ||||
|         with self.assertNumQueries(0): | ||||
|             obj.question | ||||
|  | ||||
|  | ||||
| class GetPrefetchQuerySetDeprecation(TestCase): | ||||
|     def test_generic_relation_warning(self): | ||||
|         Question.objects.create(text="test") | ||||
|   | ||||
| @@ -19,6 +19,14 @@ class Primary(models.Model): | ||||
|         return self.name | ||||
|  | ||||
|  | ||||
| class PrimaryOneToOne(models.Model): | ||||
|     name = models.CharField(max_length=50) | ||||
|     value = models.CharField(max_length=50) | ||||
|     related = models.OneToOneField( | ||||
|         Secondary, models.CASCADE, related_name="primary_o2o" | ||||
|     ) | ||||
|  | ||||
|  | ||||
| class Child(Primary): | ||||
|     pass | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ from .models import ( | ||||
|     Child, | ||||
|     ChildProxy, | ||||
|     Primary, | ||||
|     PrimaryOneToOne, | ||||
|     RefreshPrimaryProxy, | ||||
|     Secondary, | ||||
|     ShadowChild, | ||||
| @@ -326,3 +327,28 @@ class InvalidDeferTests(SimpleTestCase): | ||||
|         ) | ||||
|         with self.assertRaisesMessage(FieldError, msg): | ||||
|             Primary.objects.only("name").select_related("related")[0] | ||||
|  | ||||
|  | ||||
| class DeferredRelationTests(TestCase): | ||||
|     @classmethod | ||||
|     def setUpTestData(cls): | ||||
|         cls.secondary = Secondary.objects.create(first="a", second="b") | ||||
|         cls.primary = PrimaryOneToOne.objects.create( | ||||
|             name="Bella", value="Baxter", related=cls.secondary | ||||
|         ) | ||||
|  | ||||
|     def test_defer_not_clear_cached_relations(self): | ||||
|         obj = Secondary.objects.defer("first").get(pk=self.secondary.pk) | ||||
|         with self.assertNumQueries(1): | ||||
|             obj.primary_o2o | ||||
|         obj.first  # Accessing a deferred field. | ||||
|         with self.assertNumQueries(0): | ||||
|             obj.primary_o2o | ||||
|  | ||||
|     def test_only_not_clear_cached_relations(self): | ||||
|         obj = Secondary.objects.only("first").get(pk=self.secondary.pk) | ||||
|         with self.assertNumQueries(1): | ||||
|             obj.primary_o2o | ||||
|         obj.second  # Accessing a deferred field. | ||||
|         with self.assertNumQueries(0): | ||||
|             obj.primary_o2o | ||||
|   | ||||
		Reference in New Issue
	
	Block a user