From 3db5b0e48554ccc76becefb86b630c9cf72be9f1 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Thu, 1 Mar 2012 00:57:01 +0000 Subject: [PATCH] Fixed #17696 - Queryset prefetch_related() ignores using() Thanks to simon29 for the report. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17605 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/contenttypes/generic.py | 2 +- django/db/models/fields/related.py | 9 ++- tests/modeltests/prefetch_related/tests.py | 89 ++++++++++++++++++++++ 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index c5137877cb..5ed81a3664 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -321,7 +321,7 @@ def create_generic_related_manager(superclass): return super(GenericRelatedObjectManager, self).get_query_set().using(db).filter(**self.core_filters) def get_prefetch_query_set(self, instances): - db = self._db or router.db_for_read(self.model) + db = self._db or router.db_for_read(self.model, instance=instances[0]) query = { '%s__pk' % self.content_type_field_name: self.content_type.id, '%s__in' % self.object_id_field_name: diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 53f9b9f8c2..e23c7dc9b0 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -239,7 +239,7 @@ class SingleRelatedObjectDescriptor(object): def get_prefetch_query_set(self, instances): vals = set(instance._get_pk_val() for instance in instances) params = {'%s__pk__in' % self.related.field.name: vals} - return (self.get_query_set(), + return (self.get_query_set(instance=instances[0]), attrgetter(self.related.field.attname), lambda obj: obj._get_pk_val(), True, @@ -322,7 +322,7 @@ class ReverseSingleRelatedObjectDescriptor(object): params = {'%s__pk__in' % self.field.rel.field_name: vals} else: params = {'%s__in' % self.field.rel.field_name: vals} - return (self.get_query_set().filter(**params), + return (self.get_query_set(instance=instances[0]).filter(**params), attrgetter(self.field.rel.field_name), attrgetter(self.field.attname), True, @@ -461,7 +461,7 @@ class ForeignRelatedObjectsDescriptor(object): return super(RelatedManager, self).get_query_set().using(db).filter(**self.core_filters) def get_prefetch_query_set(self, instances): - db = self._db or router.db_for_read(self.model) + db = self._db or router.db_for_read(self.model, instance=instances[0]) query = {'%s__%s__in' % (rel_field.name, attname): set(getattr(obj, attname) for obj in instances)} qs = super(RelatedManager, self).get_query_set().using(db).filter(**query) @@ -543,8 +543,9 @@ def create_many_related_manager(superclass, rel): return super(ManyRelatedManager, self).get_query_set().using(db)._next_is_sticky().filter(**self.core_filters) def get_prefetch_query_set(self, instances): + instance = instances[0] from django.db import connections - db = self._db or router.db_for_read(self.model) + db = self._db or router.db_for_read(instance.__class__, instance=instance) query = {'%s__pk__in' % self.query_field_name: set(obj._get_pk_val() for obj in instances)} qs = super(ManyRelatedManager, self).get_query_set().using(db)._next_is_sticky().filter(**query) diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py index 382aed68b4..0a757271e2 100644 --- a/tests/modeltests/prefetch_related/tests.py +++ b/tests/modeltests/prefetch_related/tests.py @@ -483,3 +483,92 @@ class NullableTest(TestCase): bulk = Employee.objects.prefetch_related('serfs').in_bulk([boss1.pk, boss2.pk]) for b in bulk.values(): list(b.serfs.all()) + + +class MultiDbTests(TestCase): + multi_db = True + + def test_using_is_honored_m2m(self): + B = Book.objects.using('other') + A = Author.objects.using('other') + book1 = B.create(title="Poems") + book2 = B.create(title="Jane Eyre") + book3 = B.create(title="Wuthering Heights") + book4 = B.create(title="Sense and Sensibility") + + author1 = A.create(name="Charlotte", first_book=book1) + author2 = A.create(name="Anne", first_book=book1) + author3 = A.create(name="Emily", first_book=book1) + author4 = A.create(name="Jane", first_book=book4) + + book1.authors.add(author1, author2, author3) + book2.authors.add(author1) + book3.authors.add(author3) + book4.authors.add(author4) + + # Forward + qs1 = B.prefetch_related('authors') + with self.assertNumQueries(2, using='other'): + books = "".join(["%s (%s)\n" % + (book.title, ", ".join(a.name for a in book.authors.all())) + for book in qs1]) + self.assertEqual(books, + "Poems (Charlotte, Anne, Emily)\n" + "Jane Eyre (Charlotte)\n" + "Wuthering Heights (Emily)\n" + "Sense and Sensibility (Jane)\n") + + # Reverse + qs2 = A.prefetch_related('books') + with self.assertNumQueries(2, using='other'): + authors = "".join(["%s: %s\n" % + (author.name, ", ".join(b.title for b in author.books.all())) + for author in qs2]) + self.assertEquals(authors, + "Charlotte: Poems, Jane Eyre\n" + "Anne: Poems\n" + "Emily: Poems, Wuthering Heights\n" + "Jane: Sense and Sensibility\n") + + def test_using_is_honored_fkey(self): + B = Book.objects.using('other') + A = Author.objects.using('other') + book1 = B.create(title="Poems") + book2 = B.create(title="Sense and Sensibility") + + author1 = A.create(name="Charlotte Bronte", first_book=book1) + author2 = A.create(name="Jane Austen", first_book=book2) + + # Forward + with self.assertNumQueries(2, using='other'): + books = ", ".join(a.first_book.title for a in A.prefetch_related('first_book')) + self.assertEqual("Poems, Sense and Sensibility", books) + + # Reverse + with self.assertNumQueries(2, using='other'): + books = "".join("%s (%s)\n" % + (b.title, ", ".join(a.name for a in b.first_time_authors.all())) + for b in B.prefetch_related('first_time_authors')) + self.assertEqual(books, + "Poems (Charlotte Bronte)\n" + "Sense and Sensibility (Jane Austen)\n") + + def test_using_is_honored_inheritance(self): + B = BookWithYear.objects.using('other') + A = AuthorWithAge.objects.using('other') + book1 = B.create(title="Poems", published_year=2010) + book2 = B.create(title="More poems", published_year=2011) + author1 = A.create(name='Jane', first_book=book1, age=50) + author2 = A.create(name='Tom', first_book=book1, age=49) + + # parent link + with self.assertNumQueries(2, using='other'): + authors = ", ".join(a.author.name for a in A.prefetch_related('author')) + + self.assertEqual(authors, "Jane, Tom") + + # child link + with self.assertNumQueries(2, using='other'): + ages = ", ".join(str(a.authorwithage.age) for a in A.prefetch_related('authorwithage')) + + self.assertEqual(ages, "50, 49")