[1.7.x] Fixed #21760 -- prefetch_related used an inefficient query for reverse FK.

Regression introduced by commit 9777442. Refs #21410.

Backport of d3b71b976d from master
This commit is contained in:
valtron 2014-04-11 19:27:34 -06:00 committed by Loic Bistuer
parent a6eda3a5fc
commit 6b3a8d2705
4 changed files with 26 additions and 1 deletions

View File

@ -634,6 +634,7 @@ answer newbie questions, and generally made Django that much better:
David Tulig <david.tulig@gmail.com> David Tulig <david.tulig@gmail.com>
Justine Tunney <jtunney@lobstertech.com> Justine Tunney <jtunney@lobstertech.com>
Amit Upadhyay <http://www.amitu.com/blog/> Amit Upadhyay <http://www.amitu.com/blog/>
valtron
Adam Vandenberg Adam Vandenberg
Geert Vanderkelen Geert Vanderkelen
Vasil Vangelovski Vasil Vangelovski

View File

@ -529,7 +529,9 @@ class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjec
# composite fields. In the meantime we take this practical approach to # composite fields. In the meantime we take this practical approach to
# solve a regression on 1.6 when the reverse manager in hidden # solve a regression on 1.6 when the reverse manager in hidden
# (related_name ends with a '+'). Refs #21410. # (related_name ends with a '+'). Refs #21410.
if self.field.rel.is_hidden(): # The check for len(...) == 1 is a special case that allows the query
# to be join-less and smaller. Refs #21760.
if self.field.rel.is_hidden() or len(self.field.foreign_related_fields) == 1:
query = {'%s__in' % related_field.name: set(instance_attr(inst)[0] for inst in instances)} query = {'%s__in' % related_field.name: set(instance_attr(inst)[0] for inst in instances)}
else: else:
query = {'%s__in' % self.field.related_query_name(): instances} query = {'%s__in' % self.field.related_query_name(): instances}

View File

@ -67,5 +67,9 @@ Other bugfixes and changes
* Fixed :djadmin:`changepassword` on Windows * Fixed :djadmin:`changepassword` on Windows
(`#22364 <https://code.djangoproject.com/ticket/22364>`_). (`#22364 <https://code.djangoproject.com/ticket/22364>`_).
* Fixed regression in ``prefetch_related`` that caused the related objects
query to include an unnecessary join
(`#21760 <https://code.djangoproject.com/ticket/21760>`_).
Additionally, Django's vendored version of six, :mod:`django.utils.six` has been Additionally, Django's vendored version of six, :mod:`django.utils.six` has been
upgraded to the latest release (1.6.1). upgraded to the latest release (1.6.1).

View File

@ -4,8 +4,10 @@ from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db import connection from django.db import connection
from django.db.models import Prefetch from django.db.models import Prefetch
from django.db.models.query import get_prefetcher
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.utils import six from django.utils import six
from django.utils.encoding import force_text
from .models import (Author, Book, Reader, Qualification, Teacher, Department, from .models import (Author, Book, Reader, Qualification, Teacher, Department,
TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge, TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
@ -1055,3 +1057,19 @@ class Ticket21410Tests(TestCase):
def test_bug(self): def test_bug(self):
list(Author2.objects.prefetch_related('first_book', 'favorite_books')) list(Author2.objects.prefetch_related('first_book', 'favorite_books'))
class Ticket21760Tests(TestCase):
def setUp(self):
self.rooms = []
for _ in range(3):
house = House.objects.create()
for _ in range(3):
self.rooms.append(Room.objects.create(house = house))
#@override_settings(DEBUG=True)
def test_bug(self):
prefetcher = get_prefetcher(self.rooms[0], 'house')[0]
queryset = prefetcher.get_prefetch_queryset(list(Room.objects.all()))[0]
self.assertNotIn(' JOIN ', force_text(queryset.query))