From cfb4845f061ed6e81e9b5a1873d1c08d98c4b5a9 Mon Sep 17 00:00:00 2001 From: Ming Qin Date: Sat, 18 Aug 2018 15:33:43 +0800 Subject: [PATCH] Fixed #29625 -- Made Model.refresh_from_db() clear prefetch related caches. --- django/db/models/base.py | 9 ++++++++- tests/basic/tests.py | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 3c2147ba78..9cc1af0171 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -582,7 +582,14 @@ class Model(metaclass=ModelBase): When accessing deferred fields of an instance, the deferred loading of the field will call this method. """ - if fields is not None: + if fields is None: + self._prefetched_objects_cache = {} + else: + prefetched_objects_cache = getattr(self, '_prefetched_objects_cache', ()) + for field in fields: + if field in prefetched_objects_cache: + del prefetched_objects_cache[field] + fields.remove(field) if not fields: return if any(LOOKUP_SEP in f for f in fields): diff --git a/tests/basic/tests.py b/tests/basic/tests.py index d82fc54675..2ec6ace638 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -735,3 +735,27 @@ class ModelRefreshTests(TestCase): article.save() featured.refresh_from_db() self.assertEqual(featured.article.headline, 'Parrot programs in Python 2.0') + + def test_prefetched_cache_cleared(self): + a = Article.objects.create(pub_date=datetime(2005, 7, 28)) + s = SelfRef.objects.create(article=a) + # refresh_from_db() without fields=[...] + a1_prefetched = Article.objects.prefetch_related('selfref_set').first() + self.assertCountEqual(a1_prefetched.selfref_set.all(), [s]) + s.article = None + s.save() + # Relation is cleared and prefetch cache is stale. + self.assertCountEqual(a1_prefetched.selfref_set.all(), [s]) + a1_prefetched.refresh_from_db() + # Cache was cleared and new results are available. + self.assertCountEqual(a1_prefetched.selfref_set.all(), []) + # refresh_from_db() with fields=[...] + a2_prefetched = Article.objects.prefetch_related('selfref_set').first() + self.assertCountEqual(a2_prefetched.selfref_set.all(), []) + s.article = 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']) + # Cache was cleared and new results are available. + self.assertCountEqual(a2_prefetched.selfref_set.all(), [s])