From b0ec87b8578147be4357c90eabcd2b916c780810 Mon Sep 17 00:00:00 2001 From: trontelj Date: Sat, 28 Oct 2023 00:24:09 +0200 Subject: [PATCH] Fixed #34925 -- Prevented Model.refresh_from_db() from mutating list of fields. --- django/db/models/base.py | 2 +- tests/basic/models.py | 3 +++ tests/basic/tests.py | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 64d54380da..e2becf8baf 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -691,7 +691,7 @@ class Model(AltersData, metaclass=ModelBase): self._prefetched_objects_cache = {} else: prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ()) - for field in fields: + for field in list(fields): if field in prefetched_objects_cache: del prefetched_objects_cache[field] fields.remove(field) diff --git a/tests/basic/models.py b/tests/basic/models.py index b71b60a213..6802f825ff 100644 --- a/tests/basic/models.py +++ b/tests/basic/models.py @@ -38,6 +38,9 @@ class SelfRef(models.Model): related_name="+", ) article = models.ForeignKey(Article, models.SET_NULL, null=True, blank=True) + article_cited = models.ForeignKey( + Article, models.SET_NULL, null=True, blank=True, related_name="cited" + ) def __str__(self): # This method intentionally doesn't work for all cases - part diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 3c2d1dead9..c6ad1faefd 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -926,24 +926,32 @@ class ModelRefreshTests(TestCase): def test_prefetched_cache_cleared(self): a = Article.objects.create(pub_date=datetime(2005, 7, 28)) - s = SelfRef.objects.create(article=a) + s = SelfRef.objects.create(article=a, article_cited=a) # refresh_from_db() without fields=[...] - a1_prefetched = Article.objects.prefetch_related("selfref_set").first() + a1_prefetched = Article.objects.prefetch_related("selfref_set", "cited").first() self.assertCountEqual(a1_prefetched.selfref_set.all(), [s]) + self.assertCountEqual(a1_prefetched.cited.all(), [s]) s.article = None + s.article_cited = None s.save() # Relation is cleared and prefetch cache is stale. self.assertCountEqual(a1_prefetched.selfref_set.all(), [s]) + self.assertCountEqual(a1_prefetched.cited.all(), [s]) a1_prefetched.refresh_from_db() # Cache was cleared and new results are available. self.assertCountEqual(a1_prefetched.selfref_set.all(), []) + self.assertCountEqual(a1_prefetched.cited.all(), []) # refresh_from_db() with fields=[...] - a2_prefetched = Article.objects.prefetch_related("selfref_set").first() + a2_prefetched = Article.objects.prefetch_related("selfref_set", "cited").first() self.assertCountEqual(a2_prefetched.selfref_set.all(), []) + self.assertCountEqual(a2_prefetched.cited.all(), []) s.article = a + s.article_cited = a s.save() # Relation is added and prefetch cache is stale. self.assertCountEqual(a2_prefetched.selfref_set.all(), []) - a2_prefetched.refresh_from_db(fields=["selfref_set"]) + self.assertCountEqual(a2_prefetched.cited.all(), []) + a2_prefetched.refresh_from_db(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])