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:
parent
b5ad5c628a
commit
fea9cb46aa
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
Loading…
Reference in New Issue