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.