From ee49306176a2d2f1751cb890bd607d42c7c09196 Mon Sep 17 00:00:00 2001 From: Paulo Date: Wed, 20 Sep 2017 23:27:04 -0500 Subject: [PATCH] Fixed #27710 -- Made Model.save() invalidate cached, stale relations after a primary key assignment. --- django/db/models/base.py | 4 ++++ tests/many_to_one/tests.py | 10 ++++++++++ tests/one_to_one/tests.py | 10 ++++++++++ 3 files changed, 24 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index e8ef2db913..3f4d65571d 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -674,6 +674,10 @@ class Model(metaclass=ModelBase): "save() prohibited to prevent data loss due to " "unsaved related object '%s'." % field.name ) + # If the relationship's pk was changed, clear the cached + # relationship. + if obj and obj.pk != getattr(self, field.attname): + field.delete_cached_value(self) using = using or router.db_for_write(self.__class__, instance=self) if force_insert and (force_update or update_fields): diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index 0e3395bf3a..a2ff587ab3 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -656,3 +656,13 @@ class ManyToOneTests(TestCase): self.assertEqual(city.districts.count(), 2) city.districts.remove(d2) self.assertEqual(city.districts.count(), 1) + + def test_cached_relation_invalidated_on_save(self): + """ + Model.save() invalidates stale ForeignKey relations after a primary key + assignment. + """ + self.assertEqual(self.a.reporter, self.r) # caches a.reporter + self.a.reporter_id = self.r2.pk + self.a.save() + self.assertEqual(self.a.reporter, self.r2) diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index 314c56a60f..a3c84a9980 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -507,3 +507,13 @@ class OneToOneTests(TestCase): pointer = ToFieldPointer.objects.create(target=target) self.assertSequenceEqual(ToFieldPointer.objects.filter(target=target), [pointer]) self.assertSequenceEqual(ToFieldPointer.objects.filter(pk__exact=pointer), [pointer]) + + def test_cached_relation_invalidated_on_save(self): + """ + Model.save() invalidates stale OneToOneField relations after a primary + key assignment. + """ + self.assertEqual(self.b1.place, self.p1) # caches b1.place + self.b1.place_id = self.p2.pk + self.b1.save() + self.assertEqual(self.b1.place, self.p2)