summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2012-04-11 21:11:22 +0000
committerAymeric Augustin <aymeric.augustin@m4x.org>2012-04-11 21:11:22 +0000
commit632b6a1a73bc47226e40dc0a9891c67138a2161c (patch)
tree273c63111f16d529f9cf895ae977c55d64811352
parente2548ec2a9e2f8dc733bcc72681fd28a34af4dbd (diff)
Fixed #17439 -- Prevented spurious queries for missing objects after prefetch_related has run.
That affects nullable foreign key, nullable one-to-one, and reverse one-to-one relations. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17899 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/query.py23
-rw-r--r--tests/modeltests/prefetch_related/tests.py8
2 files changed, 23 insertions, 8 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 44acadf037..efa3e45e9f 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -6,6 +6,7 @@ import copy
import itertools
import sys
+from django.core import exceptions
from django.db import connections, router, transaction, IntegrityError
from django.db.models.fields import AutoField
from django.db.models.query_utils import (Q, select_related_descend,
@@ -1677,12 +1678,19 @@ def prefetch_related_objects(result_cache, related_lookups):
# (e.g. via select_related), or hopefully some other property
# that doesn't support prefetching but needs to be traversed.
- # We replace the current list of parent objects with that list.
- obj_list = [getattr(obj, attr) for obj in obj_list]
-
- # Filter out 'None' so that we can continue with nullable
- # relations.
- obj_list = [obj for obj in obj_list if obj is not None]
+ # We replace the current list of parent objects with the list
+ # of related objects, filtering out empty or missing values so
+ # that we can continue with nullable or reverse relations.
+ new_obj_list = []
+ for obj in obj_list:
+ try:
+ new_obj = getattr(obj, attr)
+ except exceptions.ObjectDoesNotExist:
+ continue
+ if new_obj is None:
+ continue
+ new_obj_list.append(new_obj)
+ obj_list = new_obj_list
def get_prefetcher(instance, attr):
@@ -1778,8 +1786,7 @@ def prefetch_one_level(instances, prefetcher, attname):
vals = rel_obj_cache.get(instance_attr_val, [])
if single:
# Need to assign to single cache on instance
- if vals:
- setattr(obj, cache_name, vals[0])
+ setattr(obj, cache_name, vals[0] if vals else None)
else:
# Multi, attribute represents a manager with an .all() method that
# returns a QuerySet
diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py
index 035ae4aa10..c15294af1d 100644
--- a/tests/modeltests/prefetch_related/tests.py
+++ b/tests/modeltests/prefetch_related/tests.py
@@ -68,6 +68,14 @@ class PrefetchRelatedTests(TestCase):
self.assertQuerysetEqual(self.book2.authors.all(), [u"<Author: Charlotte>"])
+ def test_onetoone_reverse_no_match(self):
+ # Regression for #17439
+ with self.assertNumQueries(2):
+ book = Book.objects.prefetch_related('bookwithyear').all()[0]
+ with self.assertNumQueries(0):
+ with self.assertRaises(BookWithYear.DoesNotExist):
+ book.bookwithyear
+
def test_survives_clone(self):
with self.assertNumQueries(2):
lists = [list(b.first_time_authors.all())