Fixed #28375 -- Fixed KeyError crash on reverse prefetch of a model with OneToOneField primary key to a non-pk field.

This commit is contained in:
Paulo 2017-08-14 17:27:25 -04:00 committed by Tim Graham
parent b5ad5c628a
commit fea9cb46aa
3 changed files with 25 additions and 8 deletions

View File

@ -63,8 +63,6 @@ and two directions (forward and reverse) for a total of six combinations.
``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead. ``ReverseManyToManyDescriptor``, use ``ManyToManyDescriptor`` instead.
""" """
from operator import attrgetter
from django.db import connections, router, transaction from django.db import connections, router, transaction
from django.db.models import Q, signals from django.db.models import Q, signals
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
@ -334,11 +332,8 @@ class ReverseOneToOneDescriptor:
queryset = self.get_queryset() queryset = self.get_queryset()
queryset._add_hints(instance=instances[0]) queryset._add_hints(instance=instances[0])
rel_obj_attr = attrgetter(self.related.field.attname) rel_obj_attr = self.related.field.get_local_related_value
instance_attr = self.related.field.get_foreign_related_value
def instance_attr(obj):
return obj.pk
instances_dict = {instance_attr(inst): inst for inst in instances} instances_dict = {instance_attr(inst): inst for inst in instances}
query = {'%s__in' % self.related.field.name: instances} query = {'%s__in' % self.related.field.name: instances}
queryset = queryset.filter(**query) queryset = queryset.filter(**query)

View File

@ -65,7 +65,12 @@ class BookWithYear(Book):
class Bio(models.Model): 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) books = models.ManyToManyField(Book, blank=True)

View File

@ -81,6 +81,23 @@ class PrefetchRelatedTests(TestCase):
with self.assertRaises(BookWithYear.DoesNotExist): with self.assertRaises(BookWithYear.DoesNotExist):
book.bookwithyear 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): def test_survives_clone(self):
with self.assertNumQueries(2): with self.assertNumQueries(2):
[list(b.first_time_authors.all()) [list(b.first_time_authors.all())