Kaydet (Commit) 632b6a1a authored tarafından Aymeric Augustin's avatar Aymeric Augustin

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
üst e2548ec2
...@@ -6,6 +6,7 @@ import copy ...@@ -6,6 +6,7 @@ import copy
import itertools import itertools
import sys import sys
from django.core import exceptions
from django.db import connections, router, transaction, IntegrityError from django.db import connections, router, transaction, IntegrityError
from django.db.models.fields import AutoField from django.db.models.fields import AutoField
from django.db.models.query_utils import (Q, select_related_descend, from django.db.models.query_utils import (Q, select_related_descend,
...@@ -1677,12 +1678,19 @@ def prefetch_related_objects(result_cache, related_lookups): ...@@ -1677,12 +1678,19 @@ def prefetch_related_objects(result_cache, related_lookups):
# (e.g. via select_related), or hopefully some other property # (e.g. via select_related), or hopefully some other property
# that doesn't support prefetching but needs to be traversed. # that doesn't support prefetching but needs to be traversed.
# We replace the current list of parent objects with that list. # We replace the current list of parent objects with the list
obj_list = [getattr(obj, attr) for obj in obj_list] # of related objects, filtering out empty or missing values so
# that we can continue with nullable or reverse relations.
# Filter out 'None' so that we can continue with nullable new_obj_list = []
# relations. for obj in obj_list:
obj_list = [obj for obj in obj_list if obj is not None] 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): def get_prefetcher(instance, attr):
...@@ -1778,8 +1786,7 @@ def prefetch_one_level(instances, prefetcher, attname): ...@@ -1778,8 +1786,7 @@ def prefetch_one_level(instances, prefetcher, attname):
vals = rel_obj_cache.get(instance_attr_val, []) vals = rel_obj_cache.get(instance_attr_val, [])
if single: if single:
# Need to assign to single cache on instance # Need to assign to single cache on instance
if vals: setattr(obj, cache_name, vals[0] if vals else None)
setattr(obj, cache_name, vals[0])
else: else:
# Multi, attribute represents a manager with an .all() method that # Multi, attribute represents a manager with an .all() method that
# returns a QuerySet # returns a QuerySet
......
...@@ -68,6 +68,14 @@ class PrefetchRelatedTests(TestCase): ...@@ -68,6 +68,14 @@ class PrefetchRelatedTests(TestCase):
self.assertQuerysetEqual(self.book2.authors.all(), [u"<Author: Charlotte>"]) 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): def test_survives_clone(self):
with self.assertNumQueries(2): with self.assertNumQueries(2):
lists = [list(b.first_time_authors.all()) lists = [list(b.first_time_authors.all())
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment