diff --git a/django/db/models/base.py b/django/db/models/base.py index 751f42bb9b..89faf9d1e1 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -676,9 +676,9 @@ 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): + # If the relationship's pk/to_field was changed, clear the + # cached relationship. + if obj and getattr(obj, field.target_field.attname) != getattr(self, field.attname): field.delete_cached_value(self) using = using or router.db_for_write(self.__class__, instance=self) diff --git a/docs/releases/2.1.3.txt b/docs/releases/2.1.3.txt index bebd696d05..72ec3e4a32 100644 --- a/docs/releases/2.1.3.txt +++ b/docs/releases/2.1.3.txt @@ -17,3 +17,6 @@ Bugfixes * Fixed a regression in Django 2.0 where test databases aren't reused with ``manage.py test --keepdb`` on MySQL (:ticket:`29827`). + +* Fixed a regression where cached foreign keys that use ``to_field`` were + incorrectly cleared in ``Model.save()`` (:ticket:`29896`). diff --git a/tests/many_to_one/tests.py b/tests/many_to_one/tests.py index a2ff587ab3..b04e6ad77a 100644 --- a/tests/many_to_one/tests.py +++ b/tests/many_to_one/tests.py @@ -666,3 +666,9 @@ class ManyToOneTests(TestCase): self.a.reporter_id = self.r2.pk self.a.save() self.assertEqual(self.a.reporter, self.r2) + + def test_cached_foreign_key_with_to_field_not_cleared_by_save(self): + parent = Parent.objects.create(name='a') + child = ToFieldChild.objects.create(parent=parent) + with self.assertNumQueries(0): + self.assertIs(child.parent, parent)