mirror of https://github.com/django/django.git
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
This commit is contained in:
parent
e2548ec2a9
commit
632b6a1a73
|
@ -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):
|
||||||
# (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):
|
||||||
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):
|
||||||
|
|
||||||
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())
|
||||||
|
|
Loading…
Reference in New Issue