If a queryset does:
- select_related through a reverse one-to-one
- prefetch_related through that one-to-one then another relation
and some objects don't have a related object through the reverse one-to-one, iterating the queryset raises an exception.
This is easier to demonstrate with a test case:
-
diff --git a/tests/prefetch_related/models.py b/tests/prefetch_related/models.py
index 8fec5d4..ef2eeb7 100644
a
|
b
|
class BookWithYear(Book):
|
66 | 66 | AuthorWithAge, related_name='books_with_year') |
67 | 67 | |
68 | 68 | |
| 69 | class Bio(models.Model): |
| 70 | author = models.OneToOneField(Author) |
| 71 | books = models.ManyToManyField(Book, blank=True) |
| 72 | |
| 73 | |
69 | 74 | @python_2_unicode_compatible |
70 | 75 | class Reader(models.Model): |
71 | 76 | name = models.CharField(max_length=50) |
-
diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py
index 6732e45..0cdd8db 100644
a
|
b
|
from django.test import TestCase, override_settings
|
9 | 9 | from django.utils import six |
10 | 10 | from django.utils.encoding import force_text |
11 | 11 | |
12 | | from .models import (Author, Book, Reader, Qualification, Teacher, Department, |
| 12 | from .models import (Author, Bio, Book, Reader, Qualification, Teacher, Department, |
13 | 13 | TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge, |
14 | 14 | BookWithYear, BookReview, Person, House, Room, Employee, Comment, |
15 | 15 | LessonEntry, WordEntry, Author2) |
… |
… |
class PrefetchRelatedTests(TestCase):
|
192 | 192 | ["Amy"], |
193 | 193 | ["Amy", "Belinda"]]) |
194 | 194 | |
| 195 | def test_reverse_one_to_one_then_m2m(self): |
| 196 | list(Author.objects.prefetch_related('bio__books').select_related('bio')) |
| 197 | |
195 | 198 | def test_attribute_error(self): |
196 | 199 | qs = Reader.objects.all().prefetch_related('books_read__xyz') |
197 | 200 | with self.assertRaises(AttributeError) as cm: |
This test fails with:
Traceback (most recent call last):
File "/Users/myk/Documents/dev/django/tests/prefetch_related/tests.py", line 196, in test_reverse_one_to_one_then_m2m
list(Author.objects.prefetch_related('bio__books').select_related('bio'))
File "/Users/myk/Documents/dev/django/django/db/models/query.py", line 141, in __iter__
self._fetch_all()
File "/Users/myk/Documents/dev/django/django/db/models/query.py", line 965, in _fetch_all
self._prefetch_related_objects()
File "/Users/myk/Documents/dev/django/django/db/models/query.py", line 608, in _prefetch_related_objects
prefetch_related_objects(self._result_cache, self._prefetch_related_lookups)
File "/Users/myk/Documents/dev/django/django/db/models/query.py", line 1793, in prefetch_related_objects
elif isinstance(getattr(first_obj, through_attr), list):
File "/Users/myk/Documents/dev/django/django/db/models/fields/related.py", line 420, in __get__
self.related.get_accessor_name()
RelatedObjectDoesNotExist: Author has no bio.
The regression exists in master and 1.7.x.
PR https://github.com/django/django/pull/2681.