From fea9cb46aacc73cabac883a806ccb7fdc1f979dd Mon Sep 17 00:00:00 2001 From: Paulo 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())