summaryrefslogtreecommitdiff
path: root/tests/prefetch_related/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/prefetch_related/tests.py')
-rw-r--r--tests/prefetch_related/tests.py620
1 files changed, 620 insertions, 0 deletions
diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py
new file mode 100644
index 0000000000..e81560f01f
--- /dev/null
+++ b/tests/prefetch_related/tests.py
@@ -0,0 +1,620 @@
+from __future__ import absolute_import, unicode_literals
+
+from django.contrib.contenttypes.models import ContentType
+from django.db import connection
+from django.test import TestCase
+from django.test.utils import override_settings
+from django.utils import six
+
+from .models import (Author, Book, Reader, Qualification, Teacher, Department,
+ TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors, AuthorWithAge,
+ BookWithYear, BookReview, Person, House, Room, Employee, Comment)
+
+
+class PrefetchRelatedTests(TestCase):
+
+ def setUp(self):
+
+ self.book1 = Book.objects.create(title="Poems")
+ self.book2 = Book.objects.create(title="Jane Eyre")
+ self.book3 = Book.objects.create(title="Wuthering Heights")
+ self.book4 = Book.objects.create(title="Sense and Sensibility")
+
+ self.author1 = Author.objects.create(name="Charlotte",
+ first_book=self.book1)
+ self.author2 = Author.objects.create(name="Anne",
+ first_book=self.book1)
+ self.author3 = Author.objects.create(name="Emily",
+ first_book=self.book1)
+ self.author4 = Author.objects.create(name="Jane",
+ first_book=self.book4)
+
+ self.book1.authors.add(self.author1, self.author2, self.author3)
+ self.book2.authors.add(self.author1)
+ self.book3.authors.add(self.author3)
+ self.book4.authors.add(self.author4)
+
+ self.reader1 = Reader.objects.create(name="Amy")
+ self.reader2 = Reader.objects.create(name="Belinda")
+
+ self.reader1.books_read.add(self.book1, self.book4)
+ self.reader2.books_read.add(self.book2, self.book4)
+
+ def test_m2m_forward(self):
+ with self.assertNumQueries(2):
+ lists = [list(b.authors.all()) for b in Book.objects.prefetch_related('authors')]
+
+ normal_lists = [list(b.authors.all()) for b in Book.objects.all()]
+ self.assertEqual(lists, normal_lists)
+
+
+ def test_m2m_reverse(self):
+ with self.assertNumQueries(2):
+ lists = [list(a.books.all()) for a in Author.objects.prefetch_related('books')]
+
+ normal_lists = [list(a.books.all()) for a in Author.objects.all()]
+ self.assertEqual(lists, normal_lists)
+
+ def test_foreignkey_forward(self):
+ with self.assertNumQueries(2):
+ books = [a.first_book for a in Author.objects.prefetch_related('first_book')]
+
+ normal_books = [a.first_book for a in Author.objects.all()]
+ self.assertEqual(books, normal_books)
+
+ def test_foreignkey_reverse(self):
+ with self.assertNumQueries(2):
+ lists = [list(b.first_time_authors.all())
+ for b in Book.objects.prefetch_related('first_time_authors')]
+
+ self.assertQuerysetEqual(self.book2.authors.all(), ["<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())
+ for b in Book.objects.prefetch_related('first_time_authors').exclude(id=1000)]
+
+ def test_len(self):
+ with self.assertNumQueries(2):
+ qs = Book.objects.prefetch_related('first_time_authors')
+ length = len(qs)
+ lists = [list(b.first_time_authors.all())
+ for b in qs]
+
+ def test_bool(self):
+ with self.assertNumQueries(2):
+ qs = Book.objects.prefetch_related('first_time_authors')
+ x = bool(qs)
+ lists = [list(b.first_time_authors.all())
+ for b in qs]
+
+ def test_count(self):
+ with self.assertNumQueries(2):
+ qs = Book.objects.prefetch_related('first_time_authors')
+ [b.first_time_authors.count() for b in qs]
+
+ def test_exists(self):
+ with self.assertNumQueries(2):
+ qs = Book.objects.prefetch_related('first_time_authors')
+ [b.first_time_authors.exists() for b in qs]
+
+ def test_clear(self):
+ """
+ Test that we can clear the behavior by calling prefetch_related()
+ """
+ with self.assertNumQueries(5):
+ with_prefetch = Author.objects.prefetch_related('books')
+ without_prefetch = with_prefetch.prefetch_related(None)
+ lists = [list(a.books.all()) for a in without_prefetch]
+
+ def test_m2m_then_m2m(self):
+ """
+ Test we can follow a m2m and another m2m
+ """
+ with self.assertNumQueries(3):
+ qs = Author.objects.prefetch_related('books__read_by')
+ lists = [[[six.text_type(r) for r in b.read_by.all()]
+ for b in a.books.all()]
+ for a in qs]
+ self.assertEqual(lists,
+ [
+ [["Amy"], ["Belinda"]], # Charlotte - Poems, Jane Eyre
+ [["Amy"]], # Anne - Poems
+ [["Amy"], []], # Emily - Poems, Wuthering Heights
+ [["Amy", "Belinda"]], # Jane - Sense and Sense
+ ])
+
+ def test_overriding_prefetch(self):
+ with self.assertNumQueries(3):
+ qs = Author.objects.prefetch_related('books', 'books__read_by')
+ lists = [[[six.text_type(r) for r in b.read_by.all()]
+ for b in a.books.all()]
+ for a in qs]
+ self.assertEqual(lists,
+ [
+ [["Amy"], ["Belinda"]], # Charlotte - Poems, Jane Eyre
+ [["Amy"]], # Anne - Poems
+ [["Amy"], []], # Emily - Poems, Wuthering Heights
+ [["Amy", "Belinda"]], # Jane - Sense and Sense
+ ])
+ with self.assertNumQueries(3):
+ qs = Author.objects.prefetch_related('books__read_by', 'books')
+ lists = [[[six.text_type(r) for r in b.read_by.all()]
+ for b in a.books.all()]
+ for a in qs]
+ self.assertEqual(lists,
+ [
+ [["Amy"], ["Belinda"]], # Charlotte - Poems, Jane Eyre
+ [["Amy"]], # Anne - Poems
+ [["Amy"], []], # Emily - Poems, Wuthering Heights
+ [["Amy", "Belinda"]], # Jane - Sense and Sense
+ ])
+
+ def test_get(self):
+ """
+ Test that objects retrieved with .get() get the prefetch behavior.
+ """
+ # Need a double
+ with self.assertNumQueries(3):
+ author = Author.objects.prefetch_related('books__read_by').get(name="Charlotte")
+ lists = [[six.text_type(r) for r in b.read_by.all()]
+ for b in author.books.all()]
+ self.assertEqual(lists, [["Amy"], ["Belinda"]]) # Poems, Jane Eyre
+
+ def test_foreign_key_then_m2m(self):
+ """
+ Test we can follow an m2m relation after a relation like ForeignKey
+ that doesn't have many objects
+ """
+ with self.assertNumQueries(2):
+ qs = Author.objects.select_related('first_book').prefetch_related('first_book__read_by')
+ lists = [[six.text_type(r) for r in a.first_book.read_by.all()]
+ for a in qs]
+ self.assertEqual(lists, [["Amy"],
+ ["Amy"],
+ ["Amy"],
+ ["Amy", "Belinda"]])
+
+ def test_attribute_error(self):
+ qs = Reader.objects.all().prefetch_related('books_read__xyz')
+ with self.assertRaises(AttributeError) as cm:
+ list(qs)
+
+ self.assertTrue('prefetch_related' in str(cm.exception))
+
+ def test_invalid_final_lookup(self):
+ qs = Book.objects.prefetch_related('authors__name')
+ with self.assertRaises(ValueError) as cm:
+ list(qs)
+
+ self.assertTrue('prefetch_related' in str(cm.exception))
+ self.assertTrue("name" in str(cm.exception))
+
+
+class DefaultManagerTests(TestCase):
+
+ def setUp(self):
+ self.qual1 = Qualification.objects.create(name="BA")
+ self.qual2 = Qualification.objects.create(name="BSci")
+ self.qual3 = Qualification.objects.create(name="MA")
+ self.qual4 = Qualification.objects.create(name="PhD")
+
+ self.teacher1 = Teacher.objects.create(name="Mr Cleese")
+ self.teacher2 = Teacher.objects.create(name="Mr Idle")
+ self.teacher3 = Teacher.objects.create(name="Mr Chapman")
+
+ self.teacher1.qualifications.add(self.qual1, self.qual2, self.qual3, self.qual4)
+ self.teacher2.qualifications.add(self.qual1)
+ self.teacher3.qualifications.add(self.qual2)
+
+ self.dept1 = Department.objects.create(name="English")
+ self.dept2 = Department.objects.create(name="Physics")
+
+ self.dept1.teachers.add(self.teacher1, self.teacher2)
+ self.dept2.teachers.add(self.teacher1, self.teacher3)
+
+ def test_m2m_then_m2m(self):
+ with self.assertNumQueries(3):
+ # When we prefetch the teachers, and force the query, we don't want
+ # the default manager on teachers to immediately get all the related
+ # qualifications, since this will do one query per teacher.
+ qs = Department.objects.prefetch_related('teachers')
+ depts = "".join(["%s department: %s\n" %
+ (dept.name, ", ".join(six.text_type(t) for t in dept.teachers.all()))
+ for dept in qs])
+
+ self.assertEqual(depts,
+ "English department: Mr Cleese (BA, BSci, MA, PhD), Mr Idle (BA)\n"
+ "Physics department: Mr Cleese (BA, BSci, MA, PhD), Mr Chapman (BSci)\n")
+
+
+class GenericRelationTests(TestCase):
+
+ def setUp(self):
+ book1 = Book.objects.create(title="Winnie the Pooh")
+ book2 = Book.objects.create(title="Do you like green eggs and spam?")
+ book3 = Book.objects.create(title="Three Men In A Boat")
+
+ reader1 = Reader.objects.create(name="me")
+ reader2 = Reader.objects.create(name="you")
+ reader3 = Reader.objects.create(name="someone")
+
+ book1.read_by.add(reader1, reader2)
+ book2.read_by.add(reader2)
+ book3.read_by.add(reader3)
+
+ self.book1, self.book2, self.book3 = book1, book2, book3
+ self.reader1, self.reader2, self.reader3 = reader1, reader2, reader3
+
+ def test_prefetch_GFK(self):
+ TaggedItem.objects.create(tag="awesome", content_object=self.book1)
+ TaggedItem.objects.create(tag="great", content_object=self.reader1)
+ TaggedItem.objects.create(tag="stupid", content_object=self.book2)
+ TaggedItem.objects.create(tag="amazing", content_object=self.reader3)
+
+ # 1 for TaggedItem table, 1 for Book table, 1 for Reader table
+ with self.assertNumQueries(3):
+ qs = TaggedItem.objects.prefetch_related('content_object')
+ list(qs)
+
+ def test_prefetch_GFK_nonint_pk(self):
+ Comment.objects.create(comment="awesome", content_object=self.book1)
+
+ # 1 for Comment table, 1 for Book table
+ with self.assertNumQueries(2):
+ qs = Comment.objects.prefetch_related('content_object')
+ [c.content_object for c in qs]
+
+ def test_traverse_GFK(self):
+ """
+ Test that we can traverse a 'content_object' with prefetch_related() and
+ get to related objects on the other side (assuming it is suitably
+ filtered)
+ """
+ TaggedItem.objects.create(tag="awesome", content_object=self.book1)
+ TaggedItem.objects.create(tag="awesome", content_object=self.book2)
+ TaggedItem.objects.create(tag="awesome", content_object=self.book3)
+ TaggedItem.objects.create(tag="awesome", content_object=self.reader1)
+ TaggedItem.objects.create(tag="awesome", content_object=self.reader2)
+
+ ct = ContentType.objects.get_for_model(Book)
+
+ # We get 3 queries - 1 for main query, 1 for content_objects since they
+ # all use the same table, and 1 for the 'read_by' relation.
+ with self.assertNumQueries(3):
+ # If we limit to books, we know that they will have 'read_by'
+ # attributes, so the following makes sense:
+ qs = TaggedItem.objects.filter(content_type=ct, tag='awesome').prefetch_related('content_object__read_by')
+ readers_of_awesome_books = set([r.name for tag in qs
+ for r in tag.content_object.read_by.all()])
+ self.assertEqual(readers_of_awesome_books, set(["me", "you", "someone"]))
+
+ def test_nullable_GFK(self):
+ TaggedItem.objects.create(tag="awesome", content_object=self.book1,
+ created_by=self.reader1)
+ TaggedItem.objects.create(tag="great", content_object=self.book2)
+ TaggedItem.objects.create(tag="rubbish", content_object=self.book3)
+
+ with self.assertNumQueries(2):
+ result = [t.created_by for t in TaggedItem.objects.prefetch_related('created_by')]
+
+ self.assertEqual(result,
+ [t.created_by for t in TaggedItem.objects.all()])
+
+ def test_generic_relation(self):
+ b = Bookmark.objects.create(url='http://www.djangoproject.com/')
+ t1 = TaggedItem.objects.create(content_object=b, tag='django')
+ t2 = TaggedItem.objects.create(content_object=b, tag='python')
+
+ with self.assertNumQueries(2):
+ tags = [t.tag for b in Bookmark.objects.prefetch_related('tags')
+ for t in b.tags.all()]
+ self.assertEqual(sorted(tags), ["django", "python"])
+
+ def test_charfield_GFK(self):
+ b = Bookmark.objects.create(url='http://www.djangoproject.com/')
+ t1 = TaggedItem.objects.create(content_object=b, tag='django')
+ t2 = TaggedItem.objects.create(content_object=b, favorite=b, tag='python')
+
+ with self.assertNumQueries(3):
+ bookmark = Bookmark.objects.filter(pk=b.pk).prefetch_related('tags', 'favorite_tags')[0]
+ self.assertEqual(sorted([i.tag for i in bookmark.tags.all()]), ["django", "python"])
+ self.assertEqual([i.tag for i in bookmark.favorite_tags.all()], ["python"])
+
+
+class MultiTableInheritanceTest(TestCase):
+
+ def setUp(self):
+ self.book1 = BookWithYear.objects.create(
+ title="Poems", published_year=2010)
+ self.book2 = BookWithYear.objects.create(
+ title="More poems", published_year=2011)
+ self.author1 = AuthorWithAge.objects.create(
+ name='Jane', first_book=self.book1, age=50)
+ self.author2 = AuthorWithAge.objects.create(
+ name='Tom', first_book=self.book1, age=49)
+ self.author3 = AuthorWithAge.objects.create(
+ name='Robert', first_book=self.book2, age=48)
+ self.authorAddress = AuthorAddress.objects.create(
+ author=self.author1, address='SomeStreet 1')
+ self.book2.aged_authors.add(self.author2, self.author3)
+ self.br1 = BookReview.objects.create(
+ book=self.book1, notes="review book1")
+ self.br2 = BookReview.objects.create(
+ book=self.book2, notes="review book2")
+
+ def test_foreignkey(self):
+ with self.assertNumQueries(2):
+ qs = AuthorWithAge.objects.prefetch_related('addresses')
+ addresses = [[six.text_type(address) for address in obj.addresses.all()]
+ for obj in qs]
+ self.assertEqual(addresses, [[six.text_type(self.authorAddress)], [], []])
+
+ def test_foreignkey_to_inherited(self):
+ with self.assertNumQueries(2):
+ qs = BookReview.objects.prefetch_related('book')
+ titles = [obj.book.title for obj in qs]
+ self.assertEqual(titles, ["Poems", "More poems"])
+
+ def test_m2m_to_inheriting_model(self):
+ qs = AuthorWithAge.objects.prefetch_related('books_with_year')
+ with self.assertNumQueries(2):
+ lst = [[six.text_type(book) for book in author.books_with_year.all()]
+ for author in qs]
+ qs = AuthorWithAge.objects.all()
+ lst2 = [[six.text_type(book) for book in author.books_with_year.all()]
+ for author in qs]
+ self.assertEqual(lst, lst2)
+
+ qs = BookWithYear.objects.prefetch_related('aged_authors')
+ with self.assertNumQueries(2):
+ lst = [[six.text_type(author) for author in book.aged_authors.all()]
+ for book in qs]
+ qs = BookWithYear.objects.all()
+ lst2 = [[six.text_type(author) for author in book.aged_authors.all()]
+ for book in qs]
+ self.assertEqual(lst, lst2)
+
+ def test_parent_link_prefetch(self):
+ with self.assertNumQueries(2):
+ [a.author for a in AuthorWithAge.objects.prefetch_related('author')]
+
+ @override_settings(DEBUG=True)
+ def test_child_link_prefetch(self):
+ with self.assertNumQueries(2):
+ l = [a.authorwithage for a in Author.objects.prefetch_related('authorwithage')]
+
+ # Regression for #18090: the prefetching query must include an IN clause.
+ # Note that on Oracle the table name is upper case in the generated SQL,
+ # thus the .lower() call.
+ self.assertIn('authorwithage', connection.queries[-1]['sql'].lower())
+ self.assertIn(' IN ', connection.queries[-1]['sql'])
+
+ self.assertEqual(l, [a.authorwithage for a in Author.objects.all()])
+
+
+class ForeignKeyToFieldTest(TestCase):
+
+ def setUp(self):
+ self.book = Book.objects.create(title="Poems")
+ self.author1 = Author.objects.create(name='Jane', first_book=self.book)
+ self.author2 = Author.objects.create(name='Tom', first_book=self.book)
+ self.author3 = Author.objects.create(name='Robert', first_book=self.book)
+ self.authorAddress = AuthorAddress.objects.create(
+ author=self.author1, address='SomeStreet 1'
+ )
+ FavoriteAuthors.objects.create(author=self.author1,
+ likes_author=self.author2)
+ FavoriteAuthors.objects.create(author=self.author2,
+ likes_author=self.author3)
+ FavoriteAuthors.objects.create(author=self.author3,
+ likes_author=self.author1)
+
+ def test_foreignkey(self):
+ with self.assertNumQueries(2):
+ qs = Author.objects.prefetch_related('addresses')
+ addresses = [[six.text_type(address) for address in obj.addresses.all()]
+ for obj in qs]
+ self.assertEqual(addresses, [[six.text_type(self.authorAddress)], [], []])
+
+ def test_m2m(self):
+ with self.assertNumQueries(3):
+ qs = Author.objects.all().prefetch_related('favorite_authors', 'favors_me')
+ favorites = [(
+ [six.text_type(i_like) for i_like in author.favorite_authors.all()],
+ [six.text_type(likes_me) for likes_me in author.favors_me.all()]
+ ) for author in qs]
+ self.assertEqual(
+ favorites,
+ [
+ ([six.text_type(self.author2)],[six.text_type(self.author3)]),
+ ([six.text_type(self.author3)],[six.text_type(self.author1)]),
+ ([six.text_type(self.author1)],[six.text_type(self.author2)])
+ ]
+ )
+
+
+class LookupOrderingTest(TestCase):
+ """
+ Test cases that demonstrate that ordering of lookups is important, and
+ ensure it is preserved.
+ """
+
+ def setUp(self):
+ self.person1 = Person.objects.create(name="Joe")
+ self.person2 = Person.objects.create(name="Mary")
+
+ self.house1 = House.objects.create(address="123 Main St")
+ self.house2 = House.objects.create(address="45 Side St")
+ self.house3 = House.objects.create(address="6 Downing St")
+ self.house4 = House.objects.create(address="7 Regents St")
+
+ self.room1_1 = Room.objects.create(name="Dining room", house=self.house1)
+ self.room1_2 = Room.objects.create(name="Lounge", house=self.house1)
+ self.room1_3 = Room.objects.create(name="Kitchen", house=self.house1)
+
+ self.room2_1 = Room.objects.create(name="Dining room", house=self.house2)
+ self.room2_2 = Room.objects.create(name="Lounge", house=self.house2)
+
+ self.room3_1 = Room.objects.create(name="Dining room", house=self.house3)
+ self.room3_2 = Room.objects.create(name="Lounge", house=self.house3)
+ self.room3_3 = Room.objects.create(name="Kitchen", house=self.house3)
+
+ self.room4_1 = Room.objects.create(name="Dining room", house=self.house4)
+ self.room4_2 = Room.objects.create(name="Lounge", house=self.house4)
+
+ self.person1.houses.add(self.house1, self.house2)
+ self.person2.houses.add(self.house3, self.house4)
+
+ def test_order(self):
+ with self.assertNumQueries(4):
+ # The following two queries must be done in the same order as written,
+ # otherwise 'primary_house' will cause non-prefetched lookups
+ qs = Person.objects.prefetch_related('houses__rooms',
+ 'primary_house__occupants')
+ [list(p.primary_house.occupants.all()) for p in qs]
+
+
+class NullableTest(TestCase):
+
+ def setUp(self):
+ boss = Employee.objects.create(name="Peter")
+ worker1 = Employee.objects.create(name="Joe", boss=boss)
+ worker2 = Employee.objects.create(name="Angela", boss=boss)
+
+ def test_traverse_nullable(self):
+ # Because we use select_related() for 'boss', it doesn't need to be
+ # prefetched, but we can still traverse it although it contains some nulls
+ with self.assertNumQueries(2):
+ qs = Employee.objects.select_related('boss').prefetch_related('boss__serfs')
+ co_serfs = [list(e.boss.serfs.all()) if e.boss is not None else []
+ for e in qs]
+
+ qs2 = Employee.objects.select_related('boss')
+ co_serfs2 = [list(e.boss.serfs.all()) if e.boss is not None else []
+ for e in qs2]
+
+ self.assertEqual(co_serfs, co_serfs2)
+
+ def test_prefetch_nullable(self):
+ # One for main employee, one for boss, one for serfs
+ with self.assertNumQueries(3):
+ qs = Employee.objects.prefetch_related('boss__serfs')
+ co_serfs = [list(e.boss.serfs.all()) if e.boss is not None else []
+ for e in qs]
+
+ qs2 = Employee.objects.all()
+ co_serfs2 = [list(e.boss.serfs.all()) if e.boss is not None else []
+ for e in qs2]
+
+ self.assertEqual(co_serfs, co_serfs2)
+
+ def test_in_bulk(self):
+ """
+ In-bulk does correctly prefetch objects by not using .iterator()
+ directly.
+ """
+ boss1 = Employee.objects.create(name="Peter")
+ boss2 = Employee.objects.create(name="Jack")
+ with self.assertNumQueries(2):
+ # Check that prefetch is done and it does not cause any errors.
+ 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.assertEqual(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")