Skip to content

Commit fea9cb4

Browse files
czpythontimgraham
authored andcommitted
Fixed #28375 -- Fixed KeyError crash on reverse prefetch of a model with OneToOneField primary key to a non-pk field.
1 parent b5ad5c6 commit fea9cb4

File tree

3 files changed

+25
-8
lines changed

3 files changed

+25
-8
lines changed

django/db/models/fields/related_descriptors.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ class Child(Model):
6363
``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
6464
"""
6565

66-
from operator import attrgetter
67-
6866
from django.db import connections, router, transaction
6967
from django.db.models import Q, signals
7068
from django.db.models.query import QuerySet
@@ -334,11 +332,8 @@ def get_prefetch_queryset(self, instances, queryset=None):
334332
queryset = self.get_queryset()
335333
queryset._add_hints(instance=instances[0])
336334

337-
rel_obj_attr = attrgetter(self.related.field.attname)
338-
339-
def instance_attr(obj):
340-
return obj.pk
341-
335+
rel_obj_attr = self.related.field.get_local_related_value
336+
instance_attr = self.related.field.get_foreign_related_value
342337
instances_dict = {instance_attr(inst): inst for inst in instances}
343338
query = {'%s__in' % self.related.field.name: instances}
344339
queryset = queryset.filter(**query)

tests/prefetch_related/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ class BookWithYear(Book):
6565

6666

6767
class Bio(models.Model):
68-
author = models.OneToOneField(Author, models.CASCADE)
68+
author = models.OneToOneField(
69+
Author,
70+
models.CASCADE,
71+
primary_key=True,
72+
to_field='name',
73+
)
6974
books = models.ManyToManyField(Book, blank=True)
7075

7176

tests/prefetch_related/tests.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,23 @@ def test_onetoone_reverse_no_match(self):
8181
with self.assertRaises(BookWithYear.DoesNotExist):
8282
book.bookwithyear
8383

84+
def test_onetoone_reverse_with_to_field_pk(self):
85+
"""
86+
A model (Bio) with a OneToOneField primary key (author) that references
87+
a non-pk field (name) on the related model (Author) is prefetchable.
88+
"""
89+
Bio.objects.bulk_create([
90+
Bio(author=self.author1),
91+
Bio(author=self.author2),
92+
Bio(author=self.author3),
93+
])
94+
authors = Author.objects.filter(
95+
name__in=[self.author1, self.author2, self.author3],
96+
).prefetch_related('bio')
97+
with self.assertNumQueries(2):
98+
for author in authors:
99+
self.assertEqual(author.name, author.bio.author.name)
100+
84101
def test_survives_clone(self):
85102
with self.assertNumQueries(2):
86103
[list(b.first_time_authors.all())

0 commit comments

Comments
 (0)