Fixed #34925 -- Prevented Model.refresh_from_db() from mutating list of fields.

This commit is contained in:
trontelj 2023-10-28 00:24:09 +02:00 committed by Mariusz Felisiak
parent 1c6e8ec4ed
commit b0ec87b857
3 changed files with 16 additions and 5 deletions

View File

@ -691,7 +691,7 @@ class Model(AltersData, metaclass=ModelBase):
self._prefetched_objects_cache = {} self._prefetched_objects_cache = {}
else: else:
prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ()) prefetched_objects_cache = getattr(self, "_prefetched_objects_cache", ())
for field in fields: for field in list(fields):
if field in prefetched_objects_cache: if field in prefetched_objects_cache:
del prefetched_objects_cache[field] del prefetched_objects_cache[field]
fields.remove(field) fields.remove(field)

View File

@ -38,6 +38,9 @@ class SelfRef(models.Model):
related_name="+", related_name="+",
) )
article = models.ForeignKey(Article, models.SET_NULL, null=True, blank=True) 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): def __str__(self):
# This method intentionally doesn't work for all cases - part # This method intentionally doesn't work for all cases - part

View File

@ -926,24 +926,32 @@ class ModelRefreshTests(TestCase):
def test_prefetched_cache_cleared(self): def test_prefetched_cache_cleared(self):
a = Article.objects.create(pub_date=datetime(2005, 7, 28)) 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=[...] # 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.selfref_set.all(), [s])
self.assertCountEqual(a1_prefetched.cited.all(), [s])
s.article = None s.article = None
s.article_cited = None
s.save() s.save()
# Relation is cleared and prefetch cache is stale. # Relation is cleared and prefetch cache is stale.
self.assertCountEqual(a1_prefetched.selfref_set.all(), [s]) self.assertCountEqual(a1_prefetched.selfref_set.all(), [s])
self.assertCountEqual(a1_prefetched.cited.all(), [s])
a1_prefetched.refresh_from_db() a1_prefetched.refresh_from_db()
# Cache was cleared and new results are available. # Cache was cleared and new results are available.
self.assertCountEqual(a1_prefetched.selfref_set.all(), []) self.assertCountEqual(a1_prefetched.selfref_set.all(), [])
self.assertCountEqual(a1_prefetched.cited.all(), [])
# refresh_from_db() with fields=[...] # 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.selfref_set.all(), [])
self.assertCountEqual(a2_prefetched.cited.all(), [])
s.article = a s.article = a
s.article_cited = a
s.save() s.save()
# Relation is added and prefetch cache is stale. # Relation is added and prefetch cache is stale.
self.assertCountEqual(a2_prefetched.selfref_set.all(), []) 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. # Cache was cleared and new results are available.
self.assertCountEqual(a2_prefetched.selfref_set.all(), [s]) self.assertCountEqual(a2_prefetched.selfref_set.all(), [s])
self.assertCountEqual(a2_prefetched.cited.all(), [s])