Skip to content

Commit

Permalink
Fixed #35950 -- Restored refreshing of relations when fields deferred.
Browse files Browse the repository at this point in the history
Thank you to Simon Charette and Sarah Boyce for the review.

Regression in 73df8b5.
  • Loading branch information
adamchainz authored and sarahboyce committed Dec 2, 2024
1 parent 32b9e00 commit 2f6b096
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 9 deletions.
19 changes: 10 additions & 9 deletions django/db/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/5.1.4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
9 changes: 9 additions & 0 deletions tests/contenttypes_tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
8 changes: 8 additions & 0 deletions tests/defer/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 2f6b096

Please sign in to comment.