From f5233dce309543c826224be9dfa9c9f4f855f73c Mon Sep 17 00:00:00 2001
From: Jamie Matthews <jamie@dabapps.com>
Date: Tue, 4 Jan 2022 11:10:49 +0000
Subject: [PATCH] Fixed #32511 -- Corrected handling prefetched nested reverse
 relationships.

When prefetching a set of child objects related to a set of parent
objects, we usually want to populate the relationship back from the
child to the parent to avoid a query when accessing that relationship
attribute. However, there's an edge case where the child queryset
itself specifies a prefetch back to the parent. In that case, we want
to use the prefetched relationship rather than populating the reverse
relationship from the parent.
---
 AUTHORS                                       |  1 +
 .../db/models/fields/related_descriptors.py   |  5 ++--
 tests/prefetch_related/tests.py               | 26 +++++++++++++++++++
 3 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 3ca8ad40f5..77053c9568 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -423,6 +423,7 @@ answer newbie questions, and generally made Django that much better:
     James Timmins <jameshtimmins@gmail.com>
     James Turk <dev@jamesturk.net>
     James Wheare <django@sparemint.com>
+    Jamie Matthews <jamie@mtth.org>
     Jannis Leidel <jannis@leidel.info>
     Janos Guljas
     Jan Pazdziora
diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index d5aa968400..9c50ef16ce 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -646,8 +646,9 @@ def create_reverse_many_to_one_manager(superclass, rel):
             # Since we just bypassed this class' get_queryset(), we must manage
             # the reverse relation manually.
             for rel_obj in queryset:
-                instance = instances_dict[rel_obj_attr(rel_obj)]
-                setattr(rel_obj, self.field.name, instance)
+                if not self.field.is_cached(rel_obj):
+                    instance = instances_dict[rel_obj_attr(rel_obj)]
+                    setattr(rel_obj, self.field.name, instance)
             cache_name = self.field.remote_field.get_cache_name()
             return queryset, rel_obj_attr, instance_attr, False, cache_name, False
 
diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py
index 4ea7fccd5e..5040f3d886 100644
--- a/tests/prefetch_related/tests.py
+++ b/tests/prefetch_related/tests.py
@@ -1614,3 +1614,29 @@ class ReadPrefetchedObjectsCacheTests(TestCase):
         with self.assertNumQueries(4):
             # AuthorWithAge -> Author -> FavoriteAuthors, Book
             self.assertSequenceEqual(authors, [self.author1, self.author2])
+
+
+class NestedPrefetchTests(TestCase):
+    @classmethod
+    def setUpTestData(cls):
+        house = House.objects.create(name='Big house', address='123 Main St')
+        cls.room = Room.objects.create(name='Kitchen', house=house)
+
+    def test_nested_prefetch_is_not_overwritten_by_related_object(self):
+        """
+        The prefetched relationship is used rather than populating the reverse
+        relationship from the parent, when prefetching a set of child objects
+        related to a set of parent objects and the child queryset itself
+        specifies a prefetch back to the parent.
+        """
+        queryset = House.objects.only('name').prefetch_related(
+            Prefetch('rooms', queryset=Room.objects.prefetch_related(
+                Prefetch('house', queryset=House.objects.only('address')),
+            )),
+        )
+        with self.assertNumQueries(3):
+            house = queryset.first()
+
+        self.assertIs(Room.house.is_cached(self.room), True)
+        with self.assertNumQueries(0):
+            house.rooms.first().house.address