From fea9cb46aacc73cabac883a806ccb7fdc1f979dd Mon Sep 17 00:00:00 2001
From: Paulo <commonzenpython@gmail.com>
Date: Mon, 14 Aug 2017 17:27:25 -0400
Subject: [PATCH] Fixed #28375 -- Fixed KeyError crash on reverse prefetch of a
 model with OneToOneField primary key to a non-pk field.

---
 django/db/models/fields/related_descriptors.py |  9 ++-------
 tests/prefetch_related/models.py               |  7 ++++++-
 tests/prefetch_related/tests.py                | 17 +++++++++++++++++
 3 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/django/db/models/fields/related_descriptors.py b/django/db/models/fields/related_descriptors.py
index d65af02f32..189e67fab7 100644
--- a/django/db/models/fields/related_descriptors.py
+++ b/django/db/models/fields/related_descriptors.py
@@ -63,8 +63,6 @@ and two directions (forward and reverse) for a total of six combinations.
    ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
 """
 
-from operator import attrgetter
-
 from django.db import connections, router, transaction
 from django.db.models import Q, signals
 from django.db.models.query import QuerySet
@@ -334,11 +332,8 @@ class ReverseOneToOneDescriptor:
             queryset = self.get_queryset()
         queryset._add_hints(instance=instances[0])
 
-        rel_obj_attr = attrgetter(self.related.field.attname)
-
-        def instance_attr(obj):
-            return obj.pk
-
+        rel_obj_attr = self.related.field.get_local_related_value
+        instance_attr = self.related.field.get_foreign_related_value
         instances_dict = {instance_attr(inst): inst for inst in instances}
         query = {'%s__in' % self.related.field.name: instances}
         queryset = queryset.filter(**query)
diff --git a/tests/prefetch_related/models.py b/tests/prefetch_related/models.py
index 08e62c26e0..68fdc2686d 100644
--- a/tests/prefetch_related/models.py
+++ b/tests/prefetch_related/models.py
@@ -65,7 +65,12 @@ class BookWithYear(Book):
 
 
 class Bio(models.Model):
-    author = models.OneToOneField(Author, models.CASCADE)
+    author = models.OneToOneField(
+        Author,
+        models.CASCADE,
+        primary_key=True,
+        to_field='name',
+    )
     books = models.ManyToManyField(Book, blank=True)
 
 
diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py
index 85de08b184..36d60ea1f4 100644
--- a/tests/prefetch_related/tests.py
+++ b/tests/prefetch_related/tests.py
@@ -81,6 +81,23 @@ class PrefetchRelatedTests(TestCase):
             with self.assertRaises(BookWithYear.DoesNotExist):
                 book.bookwithyear
 
+    def test_onetoone_reverse_with_to_field_pk(self):
+        """
+        A model (Bio) with a OneToOneField primary key (author) that references
+        a non-pk field (name) on the related model (Author) is prefetchable.
+        """
+        Bio.objects.bulk_create([
+            Bio(author=self.author1),
+            Bio(author=self.author2),
+            Bio(author=self.author3),
+        ])
+        authors = Author.objects.filter(
+            name__in=[self.author1, self.author2, self.author3],
+        ).prefetch_related('bio')
+        with self.assertNumQueries(2):
+            for author in authors:
+                self.assertEqual(author.name, author.bio.author.name)
+
     def test_survives_clone(self):
         with self.assertNumQueries(2):
             [list(b.first_time_authors.all())