-
-
Notifications
You must be signed in to change notification settings - Fork 31.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fixed #34925 -- Prevented Model.refresh_from_db() from mutating list of fields. #17417
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello! Thank you for your contribution 💪
As it's your first contribution be sure to check out the patch review checklist.
If you're fixing a ticket from Trac make sure to set the "Has patch" flag and include a link to this PR in the ticket!
If you have any design or process questions then you can ask in the Django forum.
Welcome aboard ⛵️!
@trontelj Thanks 👍 Regression tests are required. |
The failed tests are not related to the changes I made in code. |
There's no test for the compilemessages patch though … we can't just add patches willy-nilly without confirming correct behaviour 💀 Remember… it might look correct but it's not confirmed to be correct without a red->green test. |
I'd omit the change to compilemessages and make it a separate bug/PR (or at least a separate commit in this PR). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GTM
@trontelj Thank you for this work, but as Tim mentioned, we should make a separated PR for the changes related to compilemessages. |
Thank you @trontelj for your PR! Please update it to remove the changes to |
Sorry for the bad news but unfortunately this won't make 5.0 as it's not categorised as a release blocker. |
Ah well, easy enough to continue to work around by manipulating _prefetched_objects_cache directly. |
sorry for late reply, family matters. I'll handle it over the weekend |
No problem, we understand that contributors collaborate when they can. FYI, the fixing of the |
@trontelj Hi! Could you please rebase onto main, and re-push? The other related PR has been merged now. |
6f1d553
to
b0ec87b
Compare
@trontelj Thanks 👍 Welcome aboard ⛵ I simplified tests to avoid creating a new model. |
I think the merges solution only addresses one of the problems. Since the provided fields = ["title"]
Book.objects.filter(pk=book.pk).update(title="foo")
book.refresh_from_db(fields=fields)
logger.log("Refreshed fields %s", fields) # Will log an empty list
assert boo.title == "foo"
Book.objects.filter(pk=book.pk).update(title="bar")
book.refresh_from_db(fields=fields)
assert boo.title == "bar" # Will fail because fields == [] by the point `refresh_from_db` was called Any objection to adding the following changes as well? diff --git a/django/db/models/base.py b/django/db/models/base.py
index e2becf8baf..6eaa600f10 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -691,6 +691,7 @@ def refresh_from_db(self, using=None, fields=None):
self._prefetched_objects_cache = {}
else:
prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ())
+ fields = list(fields)
for field in list(fields):
if field in prefetched_objects_cache:
del prefetched_objects_cache[field]
@@ -711,7 +712,6 @@ def refresh_from_db(self, using=None, fields=None):
# Use provided fields, if not set then reload all non-deferred fields.
deferred_fields = self.get_deferred_fields()
if fields is not None:
- fields = list(fields)
db_instance_qs = db_instance_qs.only(*fields)
elif deferred_fields:
fields = [
diff --git a/tests/basic/tests.py b/tests/basic/tests.py
index c6ad1faefd..ad82cffe8c 100644
--- a/tests/basic/tests.py
+++ b/tests/basic/tests.py
@@ -951,7 +951,9 @@ def test_prefetched_cache_cleared(self):
# Relation is added and prefetch cache is stale.
self.assertCountEqual(a2_prefetched.selfref_set.all(), [])
self.assertCountEqual(a2_prefetched.cited.all(), [])
- a2_prefetched.refresh_from_db(fields=["selfref_set", "cited"])
+ fields = ["selfref_set", "cited"]
+ a2_prefetched.refresh_from_db(fields=fields)
+ self.assertEqual(fields, ["selfref_set", "cited"])
# Cache was cleared and new results are available.
self.assertCountEqual(a2_prefetched.selfref_set.all(), [s])
self.assertCountEqual(a2_prefetched.cited.all(), [s]) |
Good catch 🎯 |
Resolve the issue of skipping elements during list iteration and subsequently removing them from the list.