From 2f6b096b83c55317c7ceef2d8d5dc3bee33293dc Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 30 Nov 2024 08:03:55 +0000 Subject: [PATCH] Fixed #35950 -- Restored refreshing of relations when fields deferred. Thank you to Simon Charette and Sarah Boyce for the review. Regression in 73df8b54a2fab53bec4c7573cda5ad8c869c2fd8. --- django/db/models/base.py | 19 ++++++++++--------- docs/releases/5.1.4.txt | 4 ++++ tests/contenttypes_tests/test_fields.py | 9 +++++++++ tests/defer/tests.py | 8 ++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index a20e88749f5a..d948cd2a1cd9 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -726,12 +726,13 @@ def refresh_from_db(self, using=None, fields=None, from_queryset=None): if fields is not None: db_instance_qs = db_instance_qs.only(*fields) elif deferred_fields: - fields = { - f.attname - for f in self._meta.concrete_fields - if f.attname not in deferred_fields - } - db_instance_qs = db_instance_qs.only(*fields) + db_instance_qs = db_instance_qs.only( + *{ + f.attname + for f in self._meta.concrete_fields + if f.attname not in deferred_fields + } + ) db_instance = db_instance_qs.get() non_loaded_fields = db_instance.get_deferred_fields() @@ -748,9 +749,9 @@ def refresh_from_db(self, using=None, fields=None, from_queryset=None): field.delete_cached_value(self) # Clear cached relations. - for field in self._meta.related_objects: - if (fields is None or field.name in fields) and field.is_cached(self): - field.delete_cached_value(self) + for rel in self._meta.related_objects: + if (fields is None or rel.name in fields) and rel.is_cached(self): + rel.delete_cached_value(self) # Clear cached private relations. for field in self._meta.private_fields: diff --git a/docs/releases/5.1.4.txt b/docs/releases/5.1.4.txt index 0c21d99566a8..44950ac76a47 100644 --- a/docs/releases/5.1.4.txt +++ b/docs/releases/5.1.4.txt @@ -12,3 +12,7 @@ Bugfixes * Fixed a crash in ``createsuperuser`` on Python 3.13+ caused by an unhandled ``OSError`` when the username could not be determined (:ticket:`35942`). + +* Fixed a regression in Django 5.1 where relational fields were not updated + when calling ``Model.refresh_from_db()`` on instances with deferred fields + (:ticket:`35950`). diff --git a/tests/contenttypes_tests/test_fields.py b/tests/contenttypes_tests/test_fields.py index ab16324fb681..fc49d59b2775 100644 --- a/tests/contenttypes_tests/test_fields.py +++ b/tests/contenttypes_tests/test_fields.py @@ -57,6 +57,15 @@ def test_clear_cached_generic_relation_explicit_fields(self): self.assertIsNot(answer.question, old_question_obj) self.assertEqual(answer.question, old_question_obj) + def test_clear_cached_generic_relation_when_deferred(self): + question = Question.objects.create(text="question") + Answer.objects.create(text="answer", question=question) + answer = Answer.objects.defer("text").get() + old_question_obj = answer.question + # The reverse relation is refreshed even when the text field is deferred. + answer.refresh_from_db() + self.assertIsNot(answer.question, old_question_obj) + class GenericRelationTests(TestCase): def test_value_to_string(self): diff --git a/tests/defer/tests.py b/tests/defer/tests.py index 3945b667bad5..989b5c63d788 100644 --- a/tests/defer/tests.py +++ b/tests/defer/tests.py @@ -290,6 +290,14 @@ def test_custom_refresh_on_deferred_loading(self): self.assertEqual(rf2.name, "new foo") self.assertEqual(rf2.value, "new bar") + def test_refresh_when_one_field_deferred(self): + s = Secondary.objects.create() + PrimaryOneToOne.objects.create(name="foo", value="bar", related=s) + s = Secondary.objects.defer("first").get() + p_before = s.primary_o2o + s.refresh_from_db() + self.assertIsNot(s.primary_o2o, p_before) + class InvalidDeferTests(SimpleTestCase): def test_invalid_defer(self):