8000 Fixed #36207 -- Fixed refresh_from_db ForeignObject relations · django/django@99a2c10 · GitHub
[go: up one dir, main page]

Skip to content

Commit 99a2c10

Browse files
committed
Fixed #36207 -- Fixed refresh_from_db ForeignObject relations
1 parent 51cab4a commit 99a2c10

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

django/db/models/base.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from django.db.models.expressions import DatabaseDefault
3434
from django.db.models.fields.composite import CompositePrimaryKey
3535
from django.db.models.fields.related import (
36+
ForeignObject,
3637
ForeignObjectRel,
3738
OneToOneField,
3839
lazy_related_operation,
@@ -680,6 +681,52 @@ def get_deferred_fields(self):
680681
if f.attname not in self.__dict__
681682
}
682683

684+
def _refresh_from_db_with_foreign_object(self):
685+
"""
686+
Refreshes the current instance from the database, taking into account
687+
ForeignObject fields and composite primary keys.
688+
This is a private helper method and should only be invoked by
689+
refresh_from_db().
690+
"""
691+
from_queryset = self.__class__.objects
692+
693+
# Collect fields from ForeignObject and PK
694+
composite_fields = [
695+
field
696+
for field in self._meta.fields
697+
if isinstance(field, ForeignObject)
698+
or (
699+
hasattr(self._meta.pk, "fields")
700+
and field.attname in [f.attname for f in self._meta.pk.fields]
701+
)
702+
or field.attname == self._meta.pk.attname
703+
]
704+
705+
# Raw data without avoiding the cache
706+
original_values = {
707+
field.attname: self.__dict__.get(field.attname)
708+
for field in composite_fields
709+
if self.__dict__.get(field.attname) is not None
710+
}
711+
712+
if not original_values:
713+
raise ValueError("Impossible to find the object: key not found.")
714+
715+
try:
716+
# Query instance from DB
717+
db_instance = from_queryset.get(**original_values)
718+
except self.__class__.DoesNotExist:
719+
raise self.__class__.DoesNotExist(
720+
f"{self.__class__.__name__} matching query does not exist. "
721+
f"Values: {original_values}"
722+
)
723+
724+
# Update instance with data from DB
725+
for field in self._meta.fields:
726+
setattr(self, field.attname, getattr(db_instance, field.attname))
727+
728+
self._state.db = db_instance._state.db
729+
683730
def refresh_from_db(self, using=None, fields=None, from_queryset=None):
684731
"""
685732
Reload field values from the database.
@@ -711,7 +758,6 @@ def refresh_from_db(self, using=None, fields=None, from_queryset=None):
711758
'Found "%s" in fields argument. Relations and transforms '
712759
"are not allowed in fields." % LOOKUP_SEP
713760
)
714-
715761
if from_queryset is None:
716762
hints = {"instance": self}
717763
from_queryset = self.__class__._base_manager.db_manager(using, hints=hints)
@@ -733,6 +779,9 @@ def refresh_from_db(self, using=None, fields=None, from_queryset=None):
733779
}
734780
)
735781

782+
if any(type(field) is ForeignObject for field in self._meta.get_fields()):
783+
return self._refresh_from_db_with_foreign_object()
784+
736785
db_instance = db_instance_qs.get()
737786
non_loaded_fields = db_instance.get_deferred_fields()
738787
for field in self._meta.concrete_fields:

tests/composite_pk/test_models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,9 @@ def test_permissions(self):
155155
self.assertEqual(4, token.permission_set.count())
156156
self.assertEqual(4, user.permission_set.count())
157157
self.assertEqual(4, comment.permission_set.count())
158+
159+
def test_refresh_foreign_object(self):
160+
comment = Comment.objects.get(pk=self.comment_1.pk)
161+
comment.user = None
162+
comment.refresh_from_db()
163+
self.assertEqual(comment.user, self.user_1)

0 commit comments

Comments
 (0)
0