1. from unittest import mock
    
  2. 
    
  3. from django.contrib.contenttypes.models import ContentType
    
  4. from django.core.exceptions import ObjectDoesNotExist
    
  5. from django.db import connection
    
  6. from django.db.models import Prefetch, QuerySet, prefetch_related_objects
    
  7. from django.db.models.query import get_prefetcher
    
  8. from django.db.models.sql import Query
    
  9. from django.test import TestCase, override_settings
    
  10. from django.test.utils import CaptureQueriesContext, ignore_warnings
    
  11. from django.utils.deprecation import RemovedInDjango50Warning
    
  12. 
    
  13. from .models import (
    
  14.     Article,
    
  15.     Author,
    
  16.     Author2,
    
  17.     AuthorAddress,
    
  18.     AuthorWithAge,
    
  19.     Bio,
    
  20.     Book,
    
  21.     Bookmark,
    
  22.     BookReview,
    
  23.     BookWithYear,
    
  24.     Comment,
    
  25.     Department,
    
  26.     Employee,
    
  27.     FavoriteAuthors,
    
  28.     House,
    
  29.     LessonEntry,
    
  30.     ModelIterableSubclass,
    
  31.     Person,
    
  32.     Qualification,
    
  33.     Reader,
    
  34.     Room,
    
  35.     TaggedItem,
    
  36.     Teacher,
    
  37.     WordEntry,
    
  38. )
    
  39. 
    
  40. 
    
  41. class TestDataMixin:
    
  42.     @classmethod
    
  43.     def setUpTestData(cls):
    
  44.         cls.book1 = Book.objects.create(title="Poems")
    
  45.         cls.book2 = Book.objects.create(title="Jane Eyre")
    
  46.         cls.book3 = Book.objects.create(title="Wuthering Heights")
    
  47.         cls.book4 = Book.objects.create(title="Sense and Sensibility")
    
  48. 
    
  49.         cls.author1 = Author.objects.create(name="Charlotte", first_book=cls.book1)
    
  50.         cls.author2 = Author.objects.create(name="Anne", first_book=cls.book1)
    
  51.         cls.author3 = Author.objects.create(name="Emily", first_book=cls.book1)
    
  52.         cls.author4 = Author.objects.create(name="Jane", first_book=cls.book4)
    
  53. 
    
  54.         cls.book1.authors.add(cls.author1, cls.author2, cls.author3)
    
  55.         cls.book2.authors.add(cls.author1)
    
  56.         cls.book3.authors.add(cls.author3)
    
  57.         cls.book4.authors.add(cls.author4)
    
  58. 
    
  59.         cls.reader1 = Reader.objects.create(name="Amy")
    
  60.         cls.reader2 = Reader.objects.create(name="Belinda")
    
  61. 
    
  62.         cls.reader1.books_read.add(cls.book1, cls.book4)
    
  63.         cls.reader2.books_read.add(cls.book2, cls.book4)
    
  64. 
    
  65. 
    
  66. class PrefetchRelatedTests(TestDataMixin, TestCase):
    
  67.     def assertWhereContains(self, sql, needle):
    
  68.         where_idx = sql.index("WHERE")
    
  69.         self.assertEqual(
    
  70.             sql.count(str(needle), where_idx),
    
  71.             1,
    
  72.             msg="WHERE clause doesn't contain %s, actual SQL: %s"
    
  73.             % (needle, sql[where_idx:]),
    
  74.         )
    
  75. 
    
  76.     def test_m2m_forward(self):
    
  77.         with self.assertNumQueries(2):
    
  78.             lists = [
    
  79.                 list(b.authors.all()) for b in Book.objects.prefetch_related("authors")
    
  80.             ]
    
  81. 
    
  82.         normal_lists = [list(b.authors.all()) for b in Book.objects.all()]
    
  83.         self.assertEqual(lists, normal_lists)
    
  84. 
    
  85.     def test_m2m_reverse(self):
    
  86.         with self.assertNumQueries(2):
    
  87.             lists = [
    
  88.                 list(a.books.all()) for a in Author.objects.prefetch_related("books")
    
  89.             ]
    
  90. 
    
  91.         normal_lists = [list(a.books.all()) for a in Author.objects.all()]
    
  92.         self.assertEqual(lists, normal_lists)
    
  93. 
    
  94.     def test_foreignkey_forward(self):
    
  95.         with self.assertNumQueries(2):
    
  96.             books = [
    
  97.                 a.first_book for a in Author.objects.prefetch_related("first_book")
    
  98.             ]
    
  99. 
    
  100.         normal_books = [a.first_book for a in Author.objects.all()]
    
  101.         self.assertEqual(books, normal_books)
    
  102. 
    
  103.     def test_foreignkey_reverse(self):
    
  104.         with self.assertNumQueries(2):
    
  105.             [
    
  106.                 list(b.first_time_authors.all())
    
  107.                 for b in Book.objects.prefetch_related("first_time_authors")
    
  108.             ]
    
  109. 
    
  110.         self.assertSequenceEqual(self.book2.authors.all(), [self.author1])
    
  111. 
    
  112.     def test_onetoone_reverse_no_match(self):
    
  113.         # Regression for #17439
    
  114.         with self.assertNumQueries(2):
    
  115.             book = Book.objects.prefetch_related("bookwithyear").all()[0]
    
  116.         with self.assertNumQueries(0):
    
  117.             with self.assertRaises(BookWithYear.DoesNotExist):
    
  118.                 book.bookwithyear
    
  119. 
    
  120.     def test_onetoone_reverse_with_to_field_pk(self):
    
  121.         """
    
  122.         A model (Bio) with a OneToOneField primary key (author) that references
    
  123.         a non-pk field (name) on the related model (Author) is prefetchable.
    
  124.         """
    
  125.         Bio.objects.bulk_create(
    
  126.             [
    
  127.                 Bio(author=self.author1),
    
  128.                 Bio(author=self.author2),
    
  129.                 Bio(author=self.author3),
    
  130.             ]
    
  131.         )
    
  132.         authors = Author.objects.filter(
    
  133.             name__in=[self.author1, self.author2, self.author3],
    
  134.         ).prefetch_related("bio")
    
  135.         with self.assertNumQueries(2):
    
  136.             for author in authors:
    
  137.                 self.assertEqual(author.name, author.bio.author.name)
    
  138. 
    
  139.     def test_survives_clone(self):
    
  140.         with self.assertNumQueries(2):
    
  141.             [
    
  142.                 list(b.first_time_authors.all())
    
  143.                 for b in Book.objects.prefetch_related("first_time_authors").exclude(
    
  144.                     id=1000
    
  145.                 )
    
  146.             ]
    
  147. 
    
  148.     def test_len(self):
    
  149.         with self.assertNumQueries(2):
    
  150.             qs = Book.objects.prefetch_related("first_time_authors")
    
  151.             len(qs)
    
  152.             [list(b.first_time_authors.all()) for b in qs]
    
  153. 
    
  154.     def test_bool(self):
    
  155.         with self.assertNumQueries(2):
    
  156.             qs = Book.objects.prefetch_related("first_time_authors")
    
  157.             bool(qs)
    
  158.             [list(b.first_time_authors.all()) for b in qs]
    
  159. 
    
  160.     def test_count(self):
    
  161.         with self.assertNumQueries(2):
    
  162.             qs = Book.objects.prefetch_related("first_time_authors")
    
  163.             [b.first_time_authors.count() for b in qs]
    
  164. 
    
  165.     def test_exists(self):
    
  166.         with self.assertNumQueries(2):
    
  167.             qs = Book.objects.prefetch_related("first_time_authors")
    
  168.             [b.first_time_authors.exists() for b in qs]
    
  169. 
    
  170.     def test_in_and_prefetch_related(self):
    
  171.         """
    
  172.         Regression test for #20242 - QuerySet "in" didn't work the first time
    
  173.         when using prefetch_related. This was fixed by the removal of chunked
    
  174.         reads from QuerySet iteration in
    
  175.         70679243d1786e03557c28929f9762a119e3ac14.
    
  176.         """
    
  177.         qs = Book.objects.prefetch_related("first_time_authors")
    
  178.         self.assertIn(qs[0], qs)
    
  179. 
    
  180.     def test_clear(self):
    
  181.         with self.assertNumQueries(5):
    
  182.             with_prefetch = Author.objects.prefetch_related("books")
    
  183.             without_prefetch = with_prefetch.prefetch_related(None)
    
  184.             [list(a.books.all()) for a in without_prefetch]
    
  185. 
    
  186.     def test_m2m_then_m2m(self):
    
  187.         """A m2m can be followed through another m2m."""
    
  188.         with self.assertNumQueries(3):
    
  189.             qs = Author.objects.prefetch_related("books__read_by")
    
  190.             lists = [
    
  191.                 [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
    
  192.             ]
    
  193.             self.assertEqual(
    
  194.                 lists,
    
  195.                 [
    
  196.                     [["Amy"], ["Belinda"]],  # Charlotte - Poems, Jane Eyre
    
  197.                     [["Amy"]],  # Anne - Poems
    
  198.                     [["Amy"], []],  # Emily - Poems, Wuthering Heights
    
  199.                     [["Amy", "Belinda"]],  # Jane - Sense and Sense
    
  200.                 ],
    
  201.             )
    
  202. 
    
  203.     def test_overriding_prefetch(self):
    
  204.         with self.assertNumQueries(3):
    
  205.             qs = Author.objects.prefetch_related("books", "books__read_by")
    
  206.             lists = [
    
  207.                 [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
    
  208.             ]
    
  209.             self.assertEqual(
    
  210.                 lists,
    
  211.                 [
    
  212.                     [["Amy"], ["Belinda"]],  # Charlotte - Poems, Jane Eyre
    
  213.                     [["Amy"]],  # Anne - Poems
    
  214.                     [["Amy"], []],  # Emily - Poems, Wuthering Heights
    
  215.                     [["Amy", "Belinda"]],  # Jane - Sense and Sense
    
  216.                 ],
    
  217.             )
    
  218.         with self.assertNumQueries(3):
    
  219.             qs = Author.objects.prefetch_related("books__read_by", "books")
    
  220.             lists = [
    
  221.                 [[str(r) for r in b.read_by.all()] for b in a.books.all()] for a in qs
    
  222.             ]
    
  223.             self.assertEqual(
    
  224.                 lists,
    
  225.                 [
    
  226.                     [["Amy"], ["Belinda"]],  # Charlotte - Poems, Jane Eyre
    
  227.                     [["Amy"]],  # Anne - Poems
    
  228.                     [["Amy"], []],  # Emily - Poems, Wuthering Heights
    
  229.                     [["Amy", "Belinda"]],  # Jane - Sense and Sense
    
  230.                 ],
    
  231.             )
    
  232. 
    
  233.     def test_get(self):
    
  234.         """
    
  235.         Objects retrieved with .get() get the prefetch behavior.
    
  236.         """
    
  237.         # Need a double
    
  238.         with self.assertNumQueries(3):
    
  239.             author = Author.objects.prefetch_related("books__read_by").get(
    
  240.                 name="Charlotte"
    
  241.             )
    
  242.             lists = [[str(r) for r in b.read_by.all()] for b in author.books.all()]
    
  243.             self.assertEqual(lists, [["Amy"], ["Belinda"]])  # Poems, Jane Eyre
    
  244. 
    
  245.     def test_foreign_key_then_m2m(self):
    
  246.         """
    
  247.         A m2m relation can be followed after a relation like ForeignKey that
    
  248.         doesn't have many objects.
    
  249.         """
    
  250.         with self.assertNumQueries(2):
    
  251.             qs = Author.objects.select_related("first_book").prefetch_related(
    
  252.                 "first_book__read_by"
    
  253.             )
    
  254.             lists = [[str(r) for r in a.first_book.read_by.all()] for a in qs]
    
  255.             self.assertEqual(lists, [["Amy"], ["Amy"], ["Amy"], ["Amy", "Belinda"]])
    
  256. 
    
  257.     def test_reverse_one_to_one_then_m2m(self):
    
  258.         """
    
  259.         A m2m relation can be followed after going through the select_related
    
  260.         reverse of an o2o.
    
  261.         """
    
  262.         qs = Author.objects.prefetch_related("bio__books").select_related("bio")
    
  263. 
    
  264.         with self.assertNumQueries(1):
    
  265.             list(qs.all())
    
  266. 
    
  267.         Bio.objects.create(author=self.author1)
    
  268.         with self.assertNumQueries(2):
    
  269.             list(qs.all())
    
  270. 
    
  271.     def test_attribute_error(self):
    
  272.         qs = Reader.objects.prefetch_related("books_read__xyz")
    
  273.         msg = (
    
  274.             "Cannot find 'xyz' on Book object, 'books_read__xyz' "
    
  275.             "is an invalid parameter to prefetch_related()"
    
  276.         )
    
  277.         with self.assertRaisesMessage(AttributeError, msg) as cm:
    
  278.             list(qs)
    
  279. 
    
  280.         self.assertIn("prefetch_related", str(cm.exception))
    
  281. 
    
  282.     def test_invalid_final_lookup(self):
    
  283.         qs = Book.objects.prefetch_related("authors__name")
    
  284.         msg = (
    
  285.             "'authors__name' does not resolve to an item that supports "
    
  286.             "prefetching - this is an invalid parameter to prefetch_related()."
    
  287.         )
    
  288.         with self.assertRaisesMessage(ValueError, msg) as cm:
    
  289.             list(qs)
    
  290. 
    
  291.         self.assertIn("prefetch_related", str(cm.exception))
    
  292.         self.assertIn("name", str(cm.exception))
    
  293. 
    
  294.     def test_prefetch_eq(self):
    
  295.         prefetch_1 = Prefetch("authors", queryset=Author.objects.all())
    
  296.         prefetch_2 = Prefetch("books", queryset=Book.objects.all())
    
  297.         self.assertEqual(prefetch_1, prefetch_1)
    
  298.         self.assertEqual(prefetch_1, mock.ANY)
    
  299.         self.assertNotEqual(prefetch_1, prefetch_2)
    
  300. 
    
  301.     def test_forward_m2m_to_attr_conflict(self):
    
  302.         msg = "to_attr=authors conflicts with a field on the Book model."
    
  303.         authors = Author.objects.all()
    
  304.         with self.assertRaisesMessage(ValueError, msg):
    
  305.             list(
    
  306.                 Book.objects.prefetch_related(
    
  307.                     Prefetch("authors", queryset=authors, to_attr="authors"),
    
  308.                 )
    
  309.             )
    
  310.         # Without the ValueError, an author was deleted due to the implicit
    
  311.         # save of the relation assignment.
    
  312.         self.assertEqual(self.book1.authors.count(), 3)
    
  313. 
    
  314.     def test_reverse_m2m_to_attr_conflict(self):
    
  315.         msg = "to_attr=books conflicts with a field on the Author model."
    
  316.         poems = Book.objects.filter(title="Poems")
    
  317.         with self.assertRaisesMessage(ValueError, msg):
    
  318.             list(
    
  319.                 Author.objects.prefetch_related(
    
  320.                     Prefetch("books", queryset=poems, to_attr="books"),
    
  321.                 )
    
  322.             )
    
  323.         # Without the ValueError, a book was deleted due to the implicit
    
  324.         # save of reverse relation assignment.
    
  325.         self.assertEqual(self.author1.books.count(), 2)
    
  326. 
    
  327.     def test_m2m_then_reverse_fk_object_ids(self):
    
  328.         with CaptureQueriesContext(connection) as queries:
    
  329.             list(Book.objects.prefetch_related("authors__addresses"))
    
  330. 
    
  331.         sql = queries[-1]["sql"]
    
  332.         self.assertWhereContains(sql, self.author1.name)
    
  333. 
    
  334.     def test_m2m_then_m2m_object_ids(self):
    
  335.         with CaptureQueriesContext(connection) as queries:
    
  336.             list(Book.objects.prefetch_related("authors__favorite_authors"))
    
  337. 
    
  338.         sql = queries[-1]["sql"]
    
  339.         self.assertWhereContains(sql, self.author1.name)
    
  340. 
    
  341.     def test_m2m_then_reverse_one_to_one_object_ids(self):
    
  342.         with CaptureQueriesContext(connection) as queries:
    
  343.             list(Book.objects.prefetch_related("authors__authorwithage"))
    
  344. 
    
  345.         sql = queries[-1]["sql"]
    
  346.         self.assertWhereContains(sql, self.author1.id)
    
  347. 
    
  348.     def test_filter_deferred(self):
    
  349.         """
    
  350.         Related filtering of prefetched querysets is deferred on m2m and
    
  351.         reverse m2o relations until necessary.
    
  352.         """
    
  353.         add_q = Query.add_q
    
  354.         for relation in ["authors", "first_time_authors"]:
    
  355.             with self.subTest(relation=relation):
    
  356.                 with mock.patch.object(
    
  357.                     Query,
    
  358.                     "add_q",
    
  359.                     autospec=True,
    
  360.                     side_effect=lambda self, q: add_q(self, q),
    
  361.                 ) as add_q_mock:
    
  362.                     list(Book.objects.prefetch_related(relation))
    
  363.                     self.assertEqual(add_q_mock.call_count, 1)
    
  364. 
    
  365.     def test_named_values_list(self):
    
  366.         qs = Author.objects.prefetch_related("books")
    
  367.         self.assertCountEqual(
    
  368.             [value.name for value in qs.values_list("name", named=True)],
    
  369.             ["Anne", "Charlotte", "Emily", "Jane"],
    
  370.         )
    
  371. 
    
  372.     def test_m2m_prefetching_iterator_with_chunks(self):
    
  373.         with self.assertNumQueries(3):
    
  374.             authors = [
    
  375.                 b.authors.first()
    
  376.                 for b in Book.objects.prefetch_related("authors").iterator(chunk_size=2)
    
  377.             ]
    
  378.         self.assertEqual(
    
  379.             authors,
    
  380.             [self.author1, self.author1, self.author3, self.author4],
    
  381.         )
    
  382. 
    
  383.     @ignore_warnings(category=RemovedInDjango50Warning)
    
  384.     def test_m2m_prefetching_iterator_without_chunks(self):
    
  385.         # prefetch_related() is ignored.
    
  386.         with self.assertNumQueries(5):
    
  387.             authors = [
    
  388.                 b.authors.first()
    
  389.                 for b in Book.objects.prefetch_related("authors").iterator()
    
  390.             ]
    
  391.         self.assertEqual(
    
  392.             authors,
    
  393.             [self.author1, self.author1, self.author3, self.author4],
    
  394.         )
    
  395. 
    
  396.     def test_m2m_prefetching_iterator_without_chunks_warning(self):
    
  397.         msg = (
    
  398.             "Using QuerySet.iterator() after prefetch_related() without "
    
  399.             "specifying chunk_size is deprecated."
    
  400.         )
    
  401.         with self.assertWarnsMessage(RemovedInDjango50Warning, msg):
    
  402.             Book.objects.prefetch_related("authors").iterator()
    
  403. 
    
  404. 
    
  405. class RawQuerySetTests(TestDataMixin, TestCase):
    
  406.     def test_basic(self):
    
  407.         with self.assertNumQueries(2):
    
  408.             books = Book.objects.raw(
    
  409.                 "SELECT * FROM prefetch_related_book WHERE id = %s", (self.book1.id,)
    
  410.             ).prefetch_related("authors")
    
  411.             book1 = list(books)[0]
    
  412. 
    
  413.         with self.assertNumQueries(0):
    
  414.             self.assertCountEqual(
    
  415.                 book1.authors.all(), [self.author1, self.author2, self.author3]
    
  416.             )
    
  417. 
    
  418.     def test_prefetch_before_raw(self):
    
  419.         with self.assertNumQueries(2):
    
  420.             books = Book.objects.prefetch_related("authors").raw(
    
  421.                 "SELECT * FROM prefetch_related_book WHERE id = %s", (self.book1.id,)
    
  422.             )
    
  423.             book1 = list(books)[0]
    
  424. 
    
  425.         with self.assertNumQueries(0):
    
  426.             self.assertCountEqual(
    
  427.                 book1.authors.all(), [self.author1, self.author2, self.author3]
    
  428.             )
    
  429. 
    
  430.     def test_clear(self):
    
  431.         with self.assertNumQueries(5):
    
  432.             with_prefetch = Author.objects.raw(
    
  433.                 "SELECT * FROM prefetch_related_author"
    
  434.             ).prefetch_related("books")
    
  435.             without_prefetch = with_prefetch.prefetch_related(None)
    
  436.             [list(a.books.all()) for a in without_prefetch]
    
  437. 
    
  438. 
    
  439. class CustomPrefetchTests(TestCase):
    
  440.     @classmethod
    
  441.     def traverse_qs(cls, obj_iter, path):
    
  442.         """
    
  443.         Helper method that returns a list containing a list of the objects in the
    
  444.         obj_iter. Then for each object in the obj_iter, the path will be
    
  445.         recursively travelled and the found objects are added to the return value.
    
  446.         """
    
  447.         ret_val = []
    
  448. 
    
  449.         if hasattr(obj_iter, "all"):
    
  450.             obj_iter = obj_iter.all()
    
  451. 
    
  452.         try:
    
  453.             iter(obj_iter)
    
  454.         except TypeError:
    
  455.             obj_iter = [obj_iter]
    
  456. 
    
  457.         for obj in obj_iter:
    
  458.             rel_objs = []
    
  459.             for part in path:
    
  460.                 if not part:
    
  461.                     continue
    
  462.                 try:
    
  463.                     related = getattr(obj, part[0])
    
  464.                 except ObjectDoesNotExist:
    
  465.                     continue
    
  466.                 if related is not None:
    
  467.                     rel_objs.extend(cls.traverse_qs(related, [part[1:]]))
    
  468.             ret_val.append((obj, rel_objs))
    
  469.         return ret_val
    
  470. 
    
  471.     @classmethod
    
  472.     def setUpTestData(cls):
    
  473.         cls.person1 = Person.objects.create(name="Joe")
    
  474.         cls.person2 = Person.objects.create(name="Mary")
    
  475. 
    
  476.         # Set main_room for each house before creating the next one for
    
  477.         # databases where supports_nullable_unique_constraints is False.
    
  478. 
    
  479.         cls.house1 = House.objects.create(
    
  480.             name="House 1", address="123 Main St", owner=cls.person1
    
  481.         )
    
  482.         cls.room1_1 = Room.objects.create(name="Dining room", house=cls.house1)
    
  483.         cls.room1_2 = Room.objects.create(name="Lounge", house=cls.house1)
    
  484.         cls.room1_3 = Room.objects.create(name="Kitchen", house=cls.house1)
    
  485.         cls.house1.main_room = cls.room1_1
    
  486.         cls.house1.save()
    
  487.         cls.person1.houses.add(cls.house1)
    
  488. 
    
  489.         cls.house2 = House.objects.create(
    
  490.             name="House 2", address="45 Side St", owner=cls.person1
    
  491.         )
    
  492.         cls.room2_1 = Room.objects.create(name="Dining room", house=cls.house2)
    
  493.         cls.room2_2 = Room.objects.create(name="Lounge", house=cls.house2)
    
  494.         cls.room2_3 = Room.objects.create(name="Kitchen", house=cls.house2)
    
  495.         cls.house2.main_room = cls.room2_1
    
  496.         cls.house2.save()
    
  497.         cls.person1.houses.add(cls.house2)
    
  498. 
    
  499.         cls.house3 = House.objects.create(
    
  500.             name="House 3", address="6 Downing St", owner=cls.person2
    
  501.         )
    
  502.         cls.room3_1 = Room.objects.create(name="Dining room", house=cls.house3)
    
  503.         cls.room3_2 = Room.objects.create(name="Lounge", house=cls.house3)
    
  504.         cls.room3_3 = Room.objects.create(name="Kitchen", house=cls.house3)
    
  505.         cls.house3.main_room = cls.room3_1
    
  506.         cls.house3.save()
    
  507.         cls.person2.houses.add(cls.house3)
    
  508. 
    
  509.         cls.house4 = House.objects.create(
    
  510.             name="house 4", address="7 Regents St", owner=cls.person2
    
  511.         )
    
  512.         cls.room4_1 = Room.objects.create(name="Dining room", house=cls.house4)
    
  513.         cls.room4_2 = Room.objects.create(name="Lounge", house=cls.house4)
    
  514.         cls.room4_3 = Room.objects.create(name="Kitchen", house=cls.house4)
    
  515.         cls.house4.main_room = cls.room4_1
    
  516.         cls.house4.save()
    
  517.         cls.person2.houses.add(cls.house4)
    
  518. 
    
  519.     def test_traverse_qs(self):
    
  520.         qs = Person.objects.prefetch_related("houses")
    
  521.         related_objs_normal = ([list(p.houses.all()) for p in qs],)
    
  522.         related_objs_from_traverse = [
    
  523.             [inner[0] for inner in o[1]] for o in self.traverse_qs(qs, [["houses"]])
    
  524.         ]
    
  525.         self.assertEqual(related_objs_normal, (related_objs_from_traverse,))
    
  526. 
    
  527.     def test_ambiguous(self):
    
  528.         # Ambiguous: Lookup was already seen with a different queryset.
    
  529.         msg = (
    
  530.             "'houses' lookup was already seen with a different queryset. You "
    
  531.             "may need to adjust the ordering of your lookups."
    
  532.         )
    
  533.         # lookup.queryset shouldn't be evaluated.
    
  534.         with self.assertNumQueries(3):
    
  535.             with self.assertRaisesMessage(ValueError, msg):
    
  536.                 self.traverse_qs(
    
  537.                     Person.objects.prefetch_related(
    
  538.                         "houses__rooms",
    
  539.                         Prefetch("houses", queryset=House.objects.all()),
    
  540.                     ),
    
  541.                     [["houses", "rooms"]],
    
  542.                 )
    
  543. 
    
  544.         # Ambiguous: Lookup houses_lst doesn't yet exist when performing
    
  545.         # houses_lst__rooms.
    
  546.         msg = (
    
  547.             "Cannot find 'houses_lst' on Person object, 'houses_lst__rooms' is "
    
  548.             "an invalid parameter to prefetch_related()"
    
  549.         )
    
  550.         with self.assertRaisesMessage(AttributeError, msg):
    
  551.             self.traverse_qs(
    
  552.                 Person.objects.prefetch_related(
    
  553.                     "houses_lst__rooms",
    
  554.                     Prefetch(
    
  555.                         "houses", queryset=House.objects.all(), to_attr="houses_lst"
    
  556.                     ),
    
  557.                 ),
    
  558.                 [["houses", "rooms"]],
    
  559.             )
    
  560. 
    
  561.         # Not ambiguous.
    
  562.         self.traverse_qs(
    
  563.             Person.objects.prefetch_related("houses__rooms", "houses"),
    
  564.             [["houses", "rooms"]],
    
  565.         )
    
  566. 
    
  567.         self.traverse_qs(
    
  568.             Person.objects.prefetch_related(
    
  569.                 "houses__rooms",
    
  570.                 Prefetch("houses", queryset=House.objects.all(), to_attr="houses_lst"),
    
  571.             ),
    
  572.             [["houses", "rooms"]],
    
  573.         )
    
  574. 
    
  575.     def test_m2m(self):
    
  576.         # Control lookups.
    
  577.         with self.assertNumQueries(2):
    
  578.             lst1 = self.traverse_qs(
    
  579.                 Person.objects.prefetch_related("houses"), [["houses"]]
    
  580.             )
    
  581. 
    
  582.         # Test lookups.
    
  583.         with self.assertNumQueries(2):
    
  584.             lst2 = self.traverse_qs(
    
  585.                 Person.objects.prefetch_related(Prefetch("houses")), [["houses"]]
    
  586.             )
    
  587.         self.assertEqual(lst1, lst2)
    
  588.         with self.assertNumQueries(2):
    
  589.             lst2 = self.traverse_qs(
    
  590.                 Person.objects.prefetch_related(
    
  591.                     Prefetch("houses", to_attr="houses_lst")
    
  592.                 ),
    
  593.                 [["houses_lst"]],
    
  594.             )
    
  595.         self.assertEqual(lst1, lst2)
    
  596. 
    
  597.     def test_reverse_m2m(self):
    
  598.         # Control lookups.
    
  599.         with self.assertNumQueries(2):
    
  600.             lst1 = self.traverse_qs(
    
  601.                 House.objects.prefetch_related("occupants"), [["occupants"]]
    
  602.             )
    
  603. 
    
  604.         # Test lookups.
    
  605.         with self.assertNumQueries(2):
    
  606.             lst2 = self.traverse_qs(
    
  607.                 House.objects.prefetch_related(Prefetch("occupants")), [["occupants"]]
    
  608.             )
    
  609.         self.assertEqual(lst1, lst2)
    
  610.         with self.assertNumQueries(2):
    
  611.             lst2 = self.traverse_qs(
    
  612.                 House.objects.prefetch_related(
    
  613.                     Prefetch("occupants", to_attr="occupants_lst")
    
  614.                 ),
    
  615.                 [["occupants_lst"]],
    
  616.             )
    
  617.         self.assertEqual(lst1, lst2)
    
  618. 
    
  619.     def test_m2m_through_fk(self):
    
  620.         # Control lookups.
    
  621.         with self.assertNumQueries(3):
    
  622.             lst1 = self.traverse_qs(
    
  623.                 Room.objects.prefetch_related("house__occupants"),
    
  624.                 [["house", "occupants"]],
    
  625.             )
    
  626. 
    
  627.         # Test lookups.
    
  628.         with self.assertNumQueries(3):
    
  629.             lst2 = self.traverse_qs(
    
  630.                 Room.objects.prefetch_related(Prefetch("house__occupants")),
    
  631.                 [["house", "occupants"]],
    
  632.             )
    
  633.         self.assertEqual(lst1, lst2)
    
  634.         with self.assertNumQueries(3):
    
  635.             lst2 = self.traverse_qs(
    
  636.                 Room.objects.prefetch_related(
    
  637.                     Prefetch("house__occupants", to_attr="occupants_lst")
    
  638.                 ),
    
  639.                 [["house", "occupants_lst"]],
    
  640.             )
    
  641.         self.assertEqual(lst1, lst2)
    
  642. 
    
  643.     def test_m2m_through_gfk(self):
    
  644.         TaggedItem.objects.create(tag="houses", content_object=self.house1)
    
  645.         TaggedItem.objects.create(tag="houses", content_object=self.house2)
    
  646. 
    
  647.         # Control lookups.
    
  648.         with self.assertNumQueries(3):
    
  649.             lst1 = self.traverse_qs(
    
  650.                 TaggedItem.objects.filter(tag="houses").prefetch_related(
    
  651.                     "content_object__rooms"
    
  652.                 ),
    
  653.                 [["content_object", "rooms"]],
    
  654.             )
    
  655. 
    
  656.         # Test lookups.
    
  657.         with self.assertNumQueries(3):
    
  658.             lst2 = self.traverse_qs(
    
  659.                 TaggedItem.objects.prefetch_related(
    
  660.                     Prefetch("content_object"),
    
  661.                     Prefetch("content_object__rooms", to_attr="rooms_lst"),
    
  662.                 ),
    
  663.                 [["content_object", "rooms_lst"]],
    
  664.             )
    
  665.         self.assertEqual(lst1, lst2)
    
  666. 
    
  667.     def test_o2m_through_m2m(self):
    
  668.         # Control lookups.
    
  669.         with self.assertNumQueries(3):
    
  670.             lst1 = self.traverse_qs(
    
  671.                 Person.objects.prefetch_related("houses", "houses__rooms"),
    
  672.                 [["houses", "rooms"]],
    
  673.             )
    
  674. 
    
  675.         # Test lookups.
    
  676.         with self.assertNumQueries(3):
    
  677.             lst2 = self.traverse_qs(
    
  678.                 Person.objects.prefetch_related(Prefetch("houses"), "houses__rooms"),
    
  679.                 [["houses", "rooms"]],
    
  680.             )
    
  681.         self.assertEqual(lst1, lst2)
    
  682.         with self.assertNumQueries(3):
    
  683.             lst2 = self.traverse_qs(
    
  684.                 Person.objects.prefetch_related(
    
  685.                     Prefetch("houses"), Prefetch("houses__rooms")
    
  686.                 ),
    
  687.                 [["houses", "rooms"]],
    
  688.             )
    
  689.         self.assertEqual(lst1, lst2)
    
  690.         with self.assertNumQueries(3):
    
  691.             lst2 = self.traverse_qs(
    
  692.                 Person.objects.prefetch_related(
    
  693.                     Prefetch("houses", to_attr="houses_lst"), "houses_lst__rooms"
    
  694.                 ),
    
  695.                 [["houses_lst", "rooms"]],
    
  696.             )
    
  697.         self.assertEqual(lst1, lst2)
    
  698.         with self.assertNumQueries(3):
    
  699.             lst2 = self.traverse_qs(
    
  700.                 Person.objects.prefetch_related(
    
  701.                     Prefetch("houses", to_attr="houses_lst"),
    
  702.                     Prefetch("houses_lst__rooms", to_attr="rooms_lst"),
    
  703.                 ),
    
  704.                 [["houses_lst", "rooms_lst"]],
    
  705.             )
    
  706.         self.assertEqual(lst1, lst2)
    
  707. 
    
  708.     def test_generic_rel(self):
    
  709.         bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
    
  710.         TaggedItem.objects.create(content_object=bookmark, tag="django")
    
  711.         TaggedItem.objects.create(
    
  712.             content_object=bookmark, favorite=bookmark, tag="python"
    
  713.         )
    
  714. 
    
  715.         # Control lookups.
    
  716.         with self.assertNumQueries(4):
    
  717.             lst1 = self.traverse_qs(
    
  718.                 Bookmark.objects.prefetch_related(
    
  719.                     "tags", "tags__content_object", "favorite_tags"
    
  720.                 ),
    
  721.                 [["tags", "content_object"], ["favorite_tags"]],
    
  722.             )
    
  723. 
    
  724.         # Test lookups.
    
  725.         with self.assertNumQueries(4):
    
  726.             lst2 = self.traverse_qs(
    
  727.                 Bookmark.objects.prefetch_related(
    
  728.                     Prefetch("tags", to_attr="tags_lst"),
    
  729.                     Prefetch("tags_lst__content_object"),
    
  730.                     Prefetch("favorite_tags"),
    
  731.                 ),
    
  732.                 [["tags_lst", "content_object"], ["favorite_tags"]],
    
  733.             )
    
  734.         self.assertEqual(lst1, lst2)
    
  735. 
    
  736.     def test_traverse_single_item_property(self):
    
  737.         # Control lookups.
    
  738.         with self.assertNumQueries(5):
    
  739.             lst1 = self.traverse_qs(
    
  740.                 Person.objects.prefetch_related(
    
  741.                     "houses__rooms",
    
  742.                     "primary_house__occupants__houses",
    
  743.                 ),
    
  744.                 [["primary_house", "occupants", "houses"]],
    
  745.             )
    
  746. 
    
  747.         # Test lookups.
    
  748.         with self.assertNumQueries(5):
    
  749.             lst2 = self.traverse_qs(
    
  750.                 Person.objects.prefetch_related(
    
  751.                     "houses__rooms",
    
  752.                     Prefetch("primary_house__occupants", to_attr="occupants_lst"),
    
  753.                     "primary_house__occupants_lst__houses",
    
  754.                 ),
    
  755.                 [["primary_house", "occupants_lst", "houses"]],
    
  756.             )
    
  757.         self.assertEqual(lst1, lst2)
    
  758. 
    
  759.     def test_traverse_multiple_items_property(self):
    
  760.         # Control lookups.
    
  761.         with self.assertNumQueries(4):
    
  762.             lst1 = self.traverse_qs(
    
  763.                 Person.objects.prefetch_related(
    
  764.                     "houses",
    
  765.                     "all_houses__occupants__houses",
    
  766.                 ),
    
  767.                 [["all_houses", "occupants", "houses"]],
    
  768.             )
    
  769. 
    
  770.         # Test lookups.
    
  771.         with self.assertNumQueries(4):
    
  772.             lst2 = self.traverse_qs(
    
  773.                 Person.objects.prefetch_related(
    
  774.                     "houses",
    
  775.                     Prefetch("all_houses__occupants", to_attr="occupants_lst"),
    
  776.                     "all_houses__occupants_lst__houses",
    
  777.                 ),
    
  778.                 [["all_houses", "occupants_lst", "houses"]],
    
  779.             )
    
  780.         self.assertEqual(lst1, lst2)
    
  781. 
    
  782.     def test_custom_qs(self):
    
  783.         # Test basic.
    
  784.         with self.assertNumQueries(2):
    
  785.             lst1 = list(Person.objects.prefetch_related("houses"))
    
  786.         with self.assertNumQueries(2):
    
  787.             lst2 = list(
    
  788.                 Person.objects.prefetch_related(
    
  789.                     Prefetch(
    
  790.                         "houses", queryset=House.objects.all(), to_attr="houses_lst"
    
  791.                     )
    
  792.                 )
    
  793.             )
    
  794.         self.assertEqual(
    
  795.             self.traverse_qs(lst1, [["houses"]]),
    
  796.             self.traverse_qs(lst2, [["houses_lst"]]),
    
  797.         )
    
  798. 
    
  799.         # Test queryset filtering.
    
  800.         with self.assertNumQueries(2):
    
  801.             lst2 = list(
    
  802.                 Person.objects.prefetch_related(
    
  803.                     Prefetch(
    
  804.                         "houses",
    
  805.                         queryset=House.objects.filter(
    
  806.                             pk__in=[self.house1.pk, self.house3.pk]
    
  807.                         ),
    
  808.                         to_attr="houses_lst",
    
  809.                     )
    
  810.                 )
    
  811.             )
    
  812.         self.assertEqual(len(lst2[0].houses_lst), 1)
    
  813.         self.assertEqual(lst2[0].houses_lst[0], self.house1)
    
  814.         self.assertEqual(len(lst2[1].houses_lst), 1)
    
  815.         self.assertEqual(lst2[1].houses_lst[0], self.house3)
    
  816. 
    
  817.         # Test flattened.
    
  818.         with self.assertNumQueries(3):
    
  819.             lst1 = list(Person.objects.prefetch_related("houses__rooms"))
    
  820.         with self.assertNumQueries(3):
    
  821.             lst2 = list(
    
  822.                 Person.objects.prefetch_related(
    
  823.                     Prefetch(
    
  824.                         "houses__rooms",
    
  825.                         queryset=Room.objects.all(),
    
  826.                         to_attr="rooms_lst",
    
  827.                     )
    
  828.                 )
    
  829.             )
    
  830.         self.assertEqual(
    
  831.             self.traverse_qs(lst1, [["houses", "rooms"]]),
    
  832.             self.traverse_qs(lst2, [["houses", "rooms_lst"]]),
    
  833.         )
    
  834. 
    
  835.         # Test inner select_related.
    
  836.         with self.assertNumQueries(3):
    
  837.             lst1 = list(Person.objects.prefetch_related("houses__owner"))
    
  838.         with self.assertNumQueries(2):
    
  839.             lst2 = list(
    
  840.                 Person.objects.prefetch_related(
    
  841.                     Prefetch("houses", queryset=House.objects.select_related("owner"))
    
  842.                 )
    
  843.             )
    
  844.         self.assertEqual(
    
  845.             self.traverse_qs(lst1, [["houses", "owner"]]),
    
  846.             self.traverse_qs(lst2, [["houses", "owner"]]),
    
  847.         )
    
  848. 
    
  849.         # Test inner prefetch.
    
  850.         inner_rooms_qs = Room.objects.filter(pk__in=[self.room1_1.pk, self.room1_2.pk])
    
  851.         houses_qs_prf = House.objects.prefetch_related(
    
  852.             Prefetch("rooms", queryset=inner_rooms_qs, to_attr="rooms_lst")
    
  853.         )
    
  854.         with self.assertNumQueries(4):
    
  855.             lst2 = list(
    
  856.                 Person.objects.prefetch_related(
    
  857.                     Prefetch(
    
  858.                         "houses",
    
  859.                         queryset=houses_qs_prf.filter(pk=self.house1.pk),
    
  860.                         to_attr="houses_lst",
    
  861.                     ),
    
  862.                     Prefetch("houses_lst__rooms_lst__main_room_of"),
    
  863.                 )
    
  864.             )
    
  865. 
    
  866.         self.assertEqual(len(lst2[0].houses_lst[0].rooms_lst), 2)
    
  867.         self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0], self.room1_1)
    
  868.         self.assertEqual(lst2[0].houses_lst[0].rooms_lst[1], self.room1_2)
    
  869.         self.assertEqual(lst2[0].houses_lst[0].rooms_lst[0].main_room_of, self.house1)
    
  870.         self.assertEqual(len(lst2[1].houses_lst), 0)
    
  871. 
    
  872.         # Test ForwardManyToOneDescriptor.
    
  873.         houses = House.objects.select_related("owner")
    
  874.         with self.assertNumQueries(6):
    
  875.             rooms = Room.objects.prefetch_related("house")
    
  876.             lst1 = self.traverse_qs(rooms, [["house", "owner"]])
    
  877.         with self.assertNumQueries(2):
    
  878.             rooms = Room.objects.prefetch_related(Prefetch("house", queryset=houses))
    
  879.             lst2 = self.traverse_qs(rooms, [["house", "owner"]])
    
  880.         self.assertEqual(lst1, lst2)
    
  881.         with self.assertNumQueries(2):
    
  882.             houses = House.objects.select_related("owner")
    
  883.             rooms = Room.objects.prefetch_related(
    
  884.                 Prefetch("house", queryset=houses, to_attr="house_attr")
    
  885.             )
    
  886.             lst2 = self.traverse_qs(rooms, [["house_attr", "owner"]])
    
  887.         self.assertEqual(lst1, lst2)
    
  888.         room = Room.objects.prefetch_related(
    
  889.             Prefetch("house", queryset=houses.filter(address="DoesNotExist"))
    
  890.         ).first()
    
  891.         with self.assertRaises(ObjectDoesNotExist):
    
  892.             getattr(room, "house")
    
  893.         room = Room.objects.prefetch_related(
    
  894.             Prefetch(
    
  895.                 "house",
    
  896.                 queryset=houses.filter(address="DoesNotExist"),
    
  897.                 to_attr="house_attr",
    
  898.             )
    
  899.         ).first()
    
  900.         self.assertIsNone(room.house_attr)
    
  901.         rooms = Room.objects.prefetch_related(
    
  902.             Prefetch("house", queryset=House.objects.only("name"))
    
  903.         )
    
  904.         with self.assertNumQueries(2):
    
  905.             getattr(rooms.first().house, "name")
    
  906.         with self.assertNumQueries(3):
    
  907.             getattr(rooms.first().house, "address")
    
  908. 
    
  909.         # Test ReverseOneToOneDescriptor.
    
  910.         houses = House.objects.select_related("owner")
    
  911.         with self.assertNumQueries(6):
    
  912.             rooms = Room.objects.prefetch_related("main_room_of")
    
  913.             lst1 = self.traverse_qs(rooms, [["main_room_of", "owner"]])
    
  914.         with self.assertNumQueries(2):
    
  915.             rooms = Room.objects.prefetch_related(
    
  916.                 Prefetch("main_room_of", queryset=houses)
    
  917.             )
    
  918.             lst2 = self.traverse_qs(rooms, [["main_room_of", "owner"]])
    
  919.         self.assertEqual(lst1, lst2)
    
  920.         with self.assertNumQueries(2):
    
  921.             rooms = list(
    
  922.                 Room.objects.prefetch_related(
    
  923.                     Prefetch(
    
  924.                         "main_room_of",
    
  925.                         queryset=houses,
    
  926.                         to_attr="main_room_of_attr",
    
  927.                     )
    
  928.                 )
    
  929.             )
    
  930.             lst2 = self.traverse_qs(rooms, [["main_room_of_attr", "owner"]])
    
  931.         self.assertEqual(lst1, lst2)
    
  932.         room = (
    
  933.             Room.objects.filter(main_room_of__isnull=False)
    
  934.             .prefetch_related(
    
  935.                 Prefetch("main_room_of", queryset=houses.filter(address="DoesNotExist"))
    
  936.             )
    
  937.             .first()
    
  938.         )
    
  939.         with self.assertRaises(ObjectDoesNotExist):
    
  940.             getattr(room, "main_room_of")
    
  941.         room = (
    
  942.             Room.objects.filter(main_room_of__isnull=False)
    
  943.             .prefetch_related(
    
  944.                 Prefetch(
    
  945.                     "main_room_of",
    
  946.                     queryset=houses.filter(address="DoesNotExist"),
    
  947.                     to_attr="main_room_of_attr",
    
  948.                 )
    
  949.             )
    
  950.             .first()
    
  951.         )
    
  952.         self.assertIsNone(room.main_room_of_attr)
    
  953. 
    
  954.         # The custom queryset filters should be applied to the queryset
    
  955.         # instance returned by the manager.
    
  956.         person = Person.objects.prefetch_related(
    
  957.             Prefetch("houses", queryset=House.objects.filter(name="House 1")),
    
  958.         ).get(pk=self.person1.pk)
    
  959.         self.assertEqual(
    
  960.             list(person.houses.all()),
    
  961.             list(person.houses.all().all()),
    
  962.         )
    
  963. 
    
  964.     def test_nested_prefetch_related_are_not_overwritten(self):
    
  965.         # Regression test for #24873
    
  966.         houses_2 = House.objects.prefetch_related(Prefetch("rooms"))
    
  967.         persons = Person.objects.prefetch_related(Prefetch("houses", queryset=houses_2))
    
  968.         houses = House.objects.prefetch_related(Prefetch("occupants", queryset=persons))
    
  969.         list(houses)  # queryset must be evaluated once to reproduce the bug.
    
  970.         self.assertEqual(
    
  971.             houses.all()[0].occupants.all()[0].houses.all()[1].rooms.all()[0],
    
  972.             self.room2_1,
    
  973.         )
    
  974. 
    
  975.     def test_nested_prefetch_related_with_duplicate_prefetcher(self):
    
  976.         """
    
  977.         Nested prefetches whose name clashes with descriptor names
    
  978.         (Person.houses here) are allowed.
    
  979.         """
    
  980.         occupants = Person.objects.prefetch_related(
    
  981.             Prefetch("houses", to_attr="some_attr_name"),
    
  982.             Prefetch("houses", queryset=House.objects.prefetch_related("main_room")),
    
  983.         )
    
  984.         houses = House.objects.prefetch_related(
    
  985.             Prefetch("occupants", queryset=occupants)
    
  986.         )
    
  987.         with self.assertNumQueries(5):
    
  988.             self.traverse_qs(list(houses), [["occupants", "houses", "main_room"]])
    
  989. 
    
  990.     def test_values_queryset(self):
    
  991.         msg = "Prefetch querysets cannot use raw(), values(), and values_list()."
    
  992.         with self.assertRaisesMessage(ValueError, msg):
    
  993.             Prefetch("houses", House.objects.values("pk"))
    
  994.         with self.assertRaisesMessage(ValueError, msg):
    
  995.             Prefetch("houses", House.objects.values_list("pk"))
    
  996.         # That error doesn't affect managers with custom ModelIterable subclasses
    
  997.         self.assertIs(
    
  998.             Teacher.objects_custom.all()._iterable_class, ModelIterableSubclass
    
  999.         )
    
  1000.         Prefetch("teachers", Teacher.objects_custom.all())
    
  1001. 
    
  1002.     def test_raw_queryset(self):
    
  1003.         msg = "Prefetch querysets cannot use raw(), values(), and values_list()."
    
  1004.         with self.assertRaisesMessage(ValueError, msg):
    
  1005.             Prefetch("houses", House.objects.raw("select pk from house"))
    
  1006. 
    
  1007.     def test_to_attr_doesnt_cache_through_attr_as_list(self):
    
  1008.         house = House.objects.prefetch_related(
    
  1009.             Prefetch("rooms", queryset=Room.objects.all(), to_attr="to_rooms"),
    
  1010.         ).get(pk=self.house3.pk)
    
  1011.         self.assertIsInstance(house.rooms.all(), QuerySet)
    
  1012. 
    
  1013.     def test_to_attr_cached_property(self):
    
  1014.         persons = Person.objects.prefetch_related(
    
  1015.             Prefetch("houses", House.objects.all(), to_attr="cached_all_houses"),
    
  1016.         )
    
  1017.         for person in persons:
    
  1018.             # To bypass caching at the related descriptor level, don't use
    
  1019.             # person.houses.all() here.
    
  1020.             all_houses = list(House.objects.filter(occupants=person))
    
  1021.             with self.assertNumQueries(0):
    
  1022.                 self.assertEqual(person.cached_all_houses, all_houses)
    
  1023. 
    
  1024.     def test_filter_deferred(self):
    
  1025.         """
    
  1026.         Related filtering of prefetched querysets is deferred until necessary.
    
  1027.         """
    
  1028.         add_q = Query.add_q
    
  1029.         with mock.patch.object(
    
  1030.             Query,
    
  1031.             "add_q",
    
  1032.             autospec=True,
    
  1033.             side_effect=lambda self, q: add_q(self, q),
    
  1034.         ) as add_q_mock:
    
  1035.             list(
    
  1036.                 House.objects.prefetch_related(
    
  1037.                     Prefetch("occupants", queryset=Person.objects.all())
    
  1038.                 )
    
  1039.             )
    
  1040.             self.assertEqual(add_q_mock.call_count, 1)
    
  1041. 
    
  1042. 
    
  1043. class DefaultManagerTests(TestCase):
    
  1044.     @classmethod
    
  1045.     def setUpTestData(cls):
    
  1046.         cls.qual1 = Qualification.objects.create(name="BA")
    
  1047.         cls.qual2 = Qualification.objects.create(name="BSci")
    
  1048.         cls.qual3 = Qualification.objects.create(name="MA")
    
  1049.         cls.qual4 = Qualification.objects.create(name="PhD")
    
  1050. 
    
  1051.         cls.teacher1 = Teacher.objects.create(name="Mr Cleese")
    
  1052.         cls.teacher2 = Teacher.objects.create(name="Mr Idle")
    
  1053.         cls.teacher3 = Teacher.objects.create(name="Mr Chapman")
    
  1054.         cls.teacher1.qualifications.add(cls.qual1, cls.qual2, cls.qual3, cls.qual4)
    
  1055.         cls.teacher2.qualifications.add(cls.qual1)
    
  1056.         cls.teacher3.qualifications.add(cls.qual2)
    
  1057. 
    
  1058.         cls.dept1 = Department.objects.create(name="English")
    
  1059.         cls.dept2 = Department.objects.create(name="Physics")
    
  1060.         cls.dept1.teachers.add(cls.teacher1, cls.teacher2)
    
  1061.         cls.dept2.teachers.add(cls.teacher1, cls.teacher3)
    
  1062. 
    
  1063.     def test_m2m_then_m2m(self):
    
  1064.         with self.assertNumQueries(3):
    
  1065.             # When we prefetch the teachers, and force the query, we don't want
    
  1066.             # the default manager on teachers to immediately get all the related
    
  1067.             # qualifications, since this will do one query per teacher.
    
  1068.             qs = Department.objects.prefetch_related("teachers")
    
  1069.             depts = "".join(
    
  1070.                 "%s department: %s\n"
    
  1071.                 % (dept.name, ", ".join(str(t) for t in dept.teachers.all()))
    
  1072.                 for dept in qs
    
  1073.             )
    
  1074. 
    
  1075.             self.assertEqual(
    
  1076.                 depts,
    
  1077.                 "English department: Mr Cleese (BA, BSci, MA, PhD), Mr Idle (BA)\n"
    
  1078.                 "Physics department: Mr Cleese (BA, BSci, MA, PhD), Mr Chapman "
    
  1079.                 "(BSci)\n",
    
  1080.             )
    
  1081. 
    
  1082. 
    
  1083. class GenericRelationTests(TestCase):
    
  1084.     @classmethod
    
  1085.     def setUpTestData(cls):
    
  1086.         book1 = Book.objects.create(title="Winnie the Pooh")
    
  1087.         book2 = Book.objects.create(title="Do you like green eggs and spam?")
    
  1088.         book3 = Book.objects.create(title="Three Men In A Boat")
    
  1089. 
    
  1090.         reader1 = Reader.objects.create(name="me")
    
  1091.         reader2 = Reader.objects.create(name="you")
    
  1092.         reader3 = Reader.objects.create(name="someone")
    
  1093. 
    
  1094.         book1.read_by.add(reader1, reader2)
    
  1095.         book2.read_by.add(reader2)
    
  1096.         book3.read_by.add(reader3)
    
  1097. 
    
  1098.         cls.book1, cls.book2, cls.book3 = book1, book2, book3
    
  1099.         cls.reader1, cls.reader2, cls.reader3 = reader1, reader2, reader3
    
  1100. 
    
  1101.     def test_prefetch_GFK(self):
    
  1102.         TaggedItem.objects.create(tag="awesome", content_object=self.book1)
    
  1103.         TaggedItem.objects.create(tag="great", content_object=self.reader1)
    
  1104.         TaggedItem.objects.create(tag="outstanding", content_object=self.book2)
    
  1105.         TaggedItem.objects.create(tag="amazing", content_object=self.reader3)
    
  1106. 
    
  1107.         # 1 for TaggedItem table, 1 for Book table, 1 for Reader table
    
  1108.         with self.assertNumQueries(3):
    
  1109.             qs = TaggedItem.objects.prefetch_related("content_object")
    
  1110.             list(qs)
    
  1111. 
    
  1112.     def test_prefetch_GFK_nonint_pk(self):
    
  1113.         Comment.objects.create(comment="awesome", content_object=self.book1)
    
  1114. 
    
  1115.         # 1 for Comment table, 1 for Book table
    
  1116.         with self.assertNumQueries(2):
    
  1117.             qs = Comment.objects.prefetch_related("content_object")
    
  1118.             [c.content_object for c in qs]
    
  1119. 
    
  1120.     def test_prefetch_GFK_uuid_pk(self):
    
  1121.         article = Article.objects.create(name="Django")
    
  1122.         Comment.objects.create(comment="awesome", content_object_uuid=article)
    
  1123.         qs = Comment.objects.prefetch_related("content_object_uuid")
    
  1124.         self.assertEqual([c.content_object_uuid for c in qs], [article])
    
  1125. 
    
  1126.     def test_prefetch_GFK_fk_pk(self):
    
  1127.         book = Book.objects.create(title="Poems")
    
  1128.         book_with_year = BookWithYear.objects.create(book=book, published_year=2019)
    
  1129.         Comment.objects.create(comment="awesome", content_object=book_with_year)
    
  1130.         qs = Comment.objects.prefetch_related("content_object")
    
  1131.         self.assertEqual([c.content_object for c in qs], [book_with_year])
    
  1132. 
    
  1133.     def test_traverse_GFK(self):
    
  1134.         """
    
  1135.         A 'content_object' can be traversed with prefetch_related() and
    
  1136.         get to related objects on the other side (assuming it is suitably
    
  1137.         filtered)
    
  1138.         """
    
  1139.         TaggedItem.objects.create(tag="awesome", content_object=self.book1)
    
  1140.         TaggedItem.objects.create(tag="awesome", content_object=self.book2)
    
  1141.         TaggedItem.objects.create(tag="awesome", content_object=self.book3)
    
  1142.         TaggedItem.objects.create(tag="awesome", content_object=self.reader1)
    
  1143.         TaggedItem.objects.create(tag="awesome", content_object=self.reader2)
    
  1144. 
    
  1145.         ct = ContentType.objects.get_for_model(Book)
    
  1146. 
    
  1147.         # We get 3 queries - 1 for main query, 1 for content_objects since they
    
  1148.         # all use the same table, and 1 for the 'read_by' relation.
    
  1149.         with self.assertNumQueries(3):
    
  1150.             # If we limit to books, we know that they will have 'read_by'
    
  1151.             # attributes, so the following makes sense:
    
  1152.             qs = TaggedItem.objects.filter(
    
  1153.                 content_type=ct, tag="awesome"
    
  1154.             ).prefetch_related("content_object__read_by")
    
  1155.             readers_of_awesome_books = {
    
  1156.                 r.name for tag in qs for r in tag.content_object.read_by.all()
    
  1157.             }
    
  1158.             self.assertEqual(readers_of_awesome_books, {"me", "you", "someone"})
    
  1159. 
    
  1160.     def test_nullable_GFK(self):
    
  1161.         TaggedItem.objects.create(
    
  1162.             tag="awesome", content_object=self.book1, created_by=self.reader1
    
  1163.         )
    
  1164.         TaggedItem.objects.create(tag="great", content_object=self.book2)
    
  1165.         TaggedItem.objects.create(tag="rubbish", content_object=self.book3)
    
  1166. 
    
  1167.         with self.assertNumQueries(2):
    
  1168.             result = [
    
  1169.                 t.created_by for t in TaggedItem.objects.prefetch_related("created_by")
    
  1170.             ]
    
  1171. 
    
  1172.         self.assertEqual(result, [t.created_by for t in TaggedItem.objects.all()])
    
  1173. 
    
  1174.     def test_generic_relation(self):
    
  1175.         bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
    
  1176.         TaggedItem.objects.create(content_object=bookmark, tag="django")
    
  1177.         TaggedItem.objects.create(content_object=bookmark, tag="python")
    
  1178. 
    
  1179.         with self.assertNumQueries(2):
    
  1180.             tags = [
    
  1181.                 t.tag
    
  1182.                 for b in Bookmark.objects.prefetch_related("tags")
    
  1183.                 for t in b.tags.all()
    
  1184.             ]
    
  1185.             self.assertEqual(sorted(tags), ["django", "python"])
    
  1186. 
    
  1187.     def test_charfield_GFK(self):
    
  1188.         b = Bookmark.objects.create(url="http://www.djangoproject.com/")
    
  1189.         TaggedItem.objects.create(content_object=b, tag="django")
    
  1190.         TaggedItem.objects.create(content_object=b, favorite=b, tag="python")
    
  1191. 
    
  1192.         with self.assertNumQueries(3):
    
  1193.             bookmark = Bookmark.objects.filter(pk=b.pk).prefetch_related(
    
  1194.                 "tags", "favorite_tags"
    
  1195.             )[0]
    
  1196.             self.assertEqual(
    
  1197.                 sorted(i.tag for i in bookmark.tags.all()), ["django", "python"]
    
  1198.             )
    
  1199.             self.assertEqual([i.tag for i in bookmark.favorite_tags.all()], ["python"])
    
  1200. 
    
  1201.     def test_custom_queryset(self):
    
  1202.         bookmark = Bookmark.objects.create(url="http://www.djangoproject.com/")
    
  1203.         django_tag = TaggedItem.objects.create(content_object=bookmark, tag="django")
    
  1204.         TaggedItem.objects.create(content_object=bookmark, tag="python")
    
  1205. 
    
  1206.         with self.assertNumQueries(2):
    
  1207.             bookmark = Bookmark.objects.prefetch_related(
    
  1208.                 Prefetch("tags", TaggedItem.objects.filter(tag="django")),
    
  1209.             ).get()
    
  1210. 
    
  1211.         with self.assertNumQueries(0):
    
  1212.             self.assertEqual(list(bookmark.tags.all()), [django_tag])
    
  1213. 
    
  1214.         # The custom queryset filters should be applied to the queryset
    
  1215.         # instance returned by the manager.
    
  1216.         self.assertEqual(list(bookmark.tags.all()), list(bookmark.tags.all().all()))
    
  1217. 
    
  1218.     def test_deleted_GFK(self):
    
  1219.         TaggedItem.objects.create(tag="awesome", content_object=self.book1)
    
  1220.         TaggedItem.objects.create(tag="awesome", content_object=self.book2)
    
  1221.         ct = ContentType.objects.get_for_model(Book)
    
  1222. 
    
  1223.         book1_pk = self.book1.pk
    
  1224.         self.book1.delete()
    
  1225. 
    
  1226.         with self.assertNumQueries(2):
    
  1227.             qs = TaggedItem.objects.filter(tag="awesome").prefetch_related(
    
  1228.                 "content_object"
    
  1229.             )
    
  1230.             result = [
    
  1231.                 (tag.object_id, tag.content_type_id, tag.content_object) for tag in qs
    
  1232.             ]
    
  1233.             self.assertEqual(
    
  1234.                 result,
    
  1235.                 [
    
  1236.                     (book1_pk, ct.pk, None),
    
  1237.                     (self.book2.pk, ct.pk, self.book2),
    
  1238.                 ],
    
  1239.             )
    
  1240. 
    
  1241. 
    
  1242. class MultiTableInheritanceTest(TestCase):
    
  1243.     @classmethod
    
  1244.     def setUpTestData(cls):
    
  1245.         cls.book1 = BookWithYear.objects.create(title="Poems", published_year=2010)
    
  1246.         cls.book2 = BookWithYear.objects.create(title="More poems", published_year=2011)
    
  1247.         cls.author1 = AuthorWithAge.objects.create(
    
  1248.             name="Jane", first_book=cls.book1, age=50
    
  1249.         )
    
  1250.         cls.author2 = AuthorWithAge.objects.create(
    
  1251.             name="Tom", first_book=cls.book1, age=49
    
  1252.         )
    
  1253.         cls.author3 = AuthorWithAge.objects.create(
    
  1254.             name="Robert", first_book=cls.book2, age=48
    
  1255.         )
    
  1256.         cls.author_address = AuthorAddress.objects.create(
    
  1257.             author=cls.author1, address="SomeStreet 1"
    
  1258.         )
    
  1259.         cls.book2.aged_authors.add(cls.author2, cls.author3)
    
  1260.         cls.br1 = BookReview.objects.create(book=cls.book1, notes="review book1")
    
  1261.         cls.br2 = BookReview.objects.create(book=cls.book2, notes="review book2")
    
  1262. 
    
  1263.     def test_foreignkey(self):
    
  1264.         with self.assertNumQueries(2):
    
  1265.             qs = AuthorWithAge.objects.prefetch_related("addresses")
    
  1266.             addresses = [
    
  1267.                 [str(address) for address in obj.addresses.all()] for obj in qs
    
  1268.             ]
    
  1269.         self.assertEqual(addresses, [[str(self.author_address)], [], []])
    
  1270. 
    
  1271.     def test_foreignkey_to_inherited(self):
    
  1272.         with self.assertNumQueries(2):
    
  1273.             qs = BookReview.objects.prefetch_related("book")
    
  1274.             titles = [obj.book.title for obj in qs]
    
  1275.         self.assertCountEqual(titles, ["Poems", "More poems"])
    
  1276. 
    
  1277.     def test_m2m_to_inheriting_model(self):
    
  1278.         qs = AuthorWithAge.objects.prefetch_related("books_with_year")
    
  1279.         with self.assertNumQueries(2):
    
  1280.             lst = [
    
  1281.                 [str(book) for book in author.books_with_year.all()] for author in qs
    
  1282.             ]
    
  1283.         qs = AuthorWithAge.objects.all()
    
  1284.         lst2 = [[str(book) for book in author.books_with_year.all()] for author in qs]
    
  1285.         self.assertEqual(lst, lst2)
    
  1286. 
    
  1287.         qs = BookWithYear.objects.prefetch_related("aged_authors")
    
  1288.         with self.assertNumQueries(2):
    
  1289.             lst = [[str(author) for author in book.aged_authors.all()] for book in qs]
    
  1290.         qs = BookWithYear.objects.all()
    
  1291.         lst2 = [[str(author) for author in book.aged_authors.all()] for book in qs]
    
  1292.         self.assertEqual(lst, lst2)
    
  1293. 
    
  1294.     def test_parent_link_prefetch(self):
    
  1295.         with self.assertNumQueries(2):
    
  1296.             [a.author for a in AuthorWithAge.objects.prefetch_related("author")]
    
  1297. 
    
  1298.     @override_settings(DEBUG=True)
    
  1299.     def test_child_link_prefetch(self):
    
  1300.         with self.assertNumQueries(2):
    
  1301.             authors = [
    
  1302.                 a.authorwithage
    
  1303.                 for a in Author.objects.prefetch_related("authorwithage")
    
  1304.             ]
    
  1305. 
    
  1306.         # Regression for #18090: the prefetching query must include an IN clause.
    
  1307.         # Note that on Oracle the table name is upper case in the generated SQL,
    
  1308.         # thus the .lower() call.
    
  1309.         self.assertIn("authorwithage", connection.queries[-1]["sql"].lower())
    
  1310.         self.assertIn(" IN ", connection.queries[-1]["sql"])
    
  1311. 
    
  1312.         self.assertEqual(authors, [a.authorwithage for a in Author.objects.all()])
    
  1313. 
    
  1314. 
    
  1315. class ForeignKeyToFieldTest(TestCase):
    
  1316.     @classmethod
    
  1317.     def setUpTestData(cls):
    
  1318.         cls.book = Book.objects.create(title="Poems")
    
  1319.         cls.author1 = Author.objects.create(name="Jane", first_book=cls.book)
    
  1320.         cls.author2 = Author.objects.create(name="Tom", first_book=cls.book)
    
  1321.         cls.author3 = Author.objects.create(name="Robert", first_book=cls.book)
    
  1322.         cls.author_address = AuthorAddress.objects.create(
    
  1323.             author=cls.author1, address="SomeStreet 1"
    
  1324.         )
    
  1325.         FavoriteAuthors.objects.create(author=cls.author1, likes_author=cls.author2)
    
  1326.         FavoriteAuthors.objects.create(author=cls.author2, likes_author=cls.author3)
    
  1327.         FavoriteAuthors.objects.create(author=cls.author3, likes_author=cls.author1)
    
  1328. 
    
  1329.     def test_foreignkey(self):
    
  1330.         with self.assertNumQueries(2):
    
  1331.             qs = Author.objects.prefetch_related("addresses")
    
  1332.             addresses = [
    
  1333.                 [str(address) for address in obj.addresses.all()] for obj in qs
    
  1334.             ]
    
  1335.         self.assertEqual(addresses, [[str(self.author_address)], [], []])
    
  1336. 
    
  1337.     def test_m2m(self):
    
  1338.         with self.assertNumQueries(3):
    
  1339.             qs = Author.objects.prefetch_related("favorite_authors", "favors_me")
    
  1340.             favorites = [
    
  1341.                 (
    
  1342.                     [str(i_like) for i_like in author.favorite_authors.all()],
    
  1343.                     [str(likes_me) for likes_me in author.favors_me.all()],
    
  1344.                 )
    
  1345.                 for author in qs
    
  1346.             ]
    
  1347.             self.assertEqual(
    
  1348.                 favorites,
    
  1349.                 [
    
  1350.                     ([str(self.author2)], [str(self.author3)]),
    
  1351.                     ([str(self.author3)], [str(self.author1)]),
    
  1352.                     ([str(self.author1)], [str(self.author2)]),
    
  1353.                 ],
    
  1354.             )
    
  1355. 
    
  1356. 
    
  1357. class LookupOrderingTest(TestCase):
    
  1358.     """
    
  1359.     Test cases that demonstrate that ordering of lookups is important, and
    
  1360.     ensure it is preserved.
    
  1361.     """
    
  1362. 
    
  1363.     @classmethod
    
  1364.     def setUpTestData(cls):
    
  1365.         person1 = Person.objects.create(name="Joe")
    
  1366.         person2 = Person.objects.create(name="Mary")
    
  1367. 
    
  1368.         # Set main_room for each house before creating the next one for
    
  1369.         # databases where supports_nullable_unique_constraints is False.
    
  1370.         house1 = House.objects.create(address="123 Main St")
    
  1371.         room1_1 = Room.objects.create(name="Dining room", house=house1)
    
  1372.         Room.objects.create(name="Lounge", house=house1)
    
  1373.         Room.objects.create(name="Kitchen", house=house1)
    
  1374.         house1.main_room = room1_1
    
  1375.         house1.save()
    
  1376.         person1.houses.add(house1)
    
  1377. 
    
  1378.         house2 = House.objects.create(address="45 Side St")
    
  1379.         room2_1 = Room.objects.create(name="Dining room", house=house2)
    
  1380.         Room.objects.create(name="Lounge", house=house2)
    
  1381.         house2.main_room = room2_1
    
  1382.         house2.save()
    
  1383.         person1.houses.add(house2)
    
  1384. 
    
  1385.         house3 = House.objects.create(address="6 Downing St")
    
  1386.         room3_1 = Room.objects.create(name="Dining room", house=house3)
    
  1387.         Room.objects.create(name="Lounge", house=house3)
    
  1388.         Room.objects.create(name="Kitchen", house=house3)
    
  1389.         house3.main_room = room3_1
    
  1390.         house3.save()
    
  1391.         person2.houses.add(house3)
    
  1392. 
    
  1393.         house4 = House.objects.create(address="7 Regents St")
    
  1394.         room4_1 = Room.objects.create(name="Dining room", house=house4)
    
  1395.         Room.objects.create(name="Lounge", house=house4)
    
  1396.         house4.main_room = room4_1
    
  1397.         house4.save()
    
  1398.         person2.houses.add(house4)
    
  1399. 
    
  1400.     def test_order(self):
    
  1401.         with self.assertNumQueries(4):
    
  1402.             # The following two queries must be done in the same order as written,
    
  1403.             # otherwise 'primary_house' will cause non-prefetched lookups
    
  1404.             qs = Person.objects.prefetch_related(
    
  1405.                 "houses__rooms", "primary_house__occupants"
    
  1406.             )
    
  1407.             [list(p.primary_house.occupants.all()) for p in qs]
    
  1408. 
    
  1409. 
    
  1410. class NullableTest(TestCase):
    
  1411.     @classmethod
    
  1412.     def setUpTestData(cls):
    
  1413.         boss = Employee.objects.create(name="Peter")
    
  1414.         Employee.objects.create(name="Joe", boss=boss)
    
  1415.         Employee.objects.create(name="Angela", boss=boss)
    
  1416. 
    
  1417.     def test_traverse_nullable(self):
    
  1418.         # Because we use select_related() for 'boss', it doesn't need to be
    
  1419.         # prefetched, but we can still traverse it although it contains some nulls
    
  1420.         with self.assertNumQueries(2):
    
  1421.             qs = Employee.objects.select_related("boss").prefetch_related("boss__serfs")
    
  1422.             co_serfs = [
    
  1423.                 list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs
    
  1424.             ]
    
  1425. 
    
  1426.         qs2 = Employee.objects.select_related("boss")
    
  1427.         co_serfs2 = [
    
  1428.             list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs2
    
  1429.         ]
    
  1430. 
    
  1431.         self.assertEqual(co_serfs, co_serfs2)
    
  1432. 
    
  1433.     def test_prefetch_nullable(self):
    
  1434.         # One for main employee, one for boss, one for serfs
    
  1435.         with self.assertNumQueries(3):
    
  1436.             qs = Employee.objects.prefetch_related("boss__serfs")
    
  1437.             co_serfs = [
    
  1438.                 list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs
    
  1439.             ]
    
  1440. 
    
  1441.         qs2 = Employee.objects.all()
    
  1442.         co_serfs2 = [
    
  1443.             list(e.boss.serfs.all()) if e.boss is not None else [] for e in qs2
    
  1444.         ]
    
  1445. 
    
  1446.         self.assertEqual(co_serfs, co_serfs2)
    
  1447. 
    
  1448.     def test_in_bulk(self):
    
  1449.         """
    
  1450.         In-bulk does correctly prefetch objects by not using .iterator()
    
  1451.         directly.
    
  1452.         """
    
  1453.         boss1 = Employee.objects.create(name="Peter")
    
  1454.         boss2 = Employee.objects.create(name="Jack")
    
  1455.         with self.assertNumQueries(2):
    
  1456.             # Prefetch is done and it does not cause any errors.
    
  1457.             bulk = Employee.objects.prefetch_related("serfs").in_bulk(
    
  1458.                 [boss1.pk, boss2.pk]
    
  1459.             )
    
  1460.             for b in bulk.values():
    
  1461.                 list(b.serfs.all())
    
  1462. 
    
  1463. 
    
  1464. class MultiDbTests(TestCase):
    
  1465.     databases = {"default", "other"}
    
  1466. 
    
  1467.     def test_using_is_honored_m2m(self):
    
  1468.         B = Book.objects.using("other")
    
  1469.         A = Author.objects.using("other")
    
  1470.         book1 = B.create(title="Poems")
    
  1471.         book2 = B.create(title="Jane Eyre")
    
  1472.         book3 = B.create(title="Wuthering Heights")
    
  1473.         book4 = B.create(title="Sense and Sensibility")
    
  1474. 
    
  1475.         author1 = A.create(name="Charlotte", first_book=book1)
    
  1476.         author2 = A.create(name="Anne", first_book=book1)
    
  1477.         author3 = A.create(name="Emily", first_book=book1)
    
  1478.         author4 = A.create(name="Jane", first_book=book4)
    
  1479. 
    
  1480.         book1.authors.add(author1, author2, author3)
    
  1481.         book2.authors.add(author1)
    
  1482.         book3.authors.add(author3)
    
  1483.         book4.authors.add(author4)
    
  1484. 
    
  1485.         # Forward
    
  1486.         qs1 = B.prefetch_related("authors")
    
  1487.         with self.assertNumQueries(2, using="other"):
    
  1488.             books = "".join(
    
  1489.                 "%s (%s)\n"
    
  1490.                 % (book.title, ", ".join(a.name for a in book.authors.all()))
    
  1491.                 for book in qs1
    
  1492.             )
    
  1493.         self.assertEqual(
    
  1494.             books,
    
  1495.             "Poems (Charlotte, Anne, Emily)\n"
    
  1496.             "Jane Eyre (Charlotte)\n"
    
  1497.             "Wuthering Heights (Emily)\n"
    
  1498.             "Sense and Sensibility (Jane)\n",
    
  1499.         )
    
  1500. 
    
  1501.         # Reverse
    
  1502.         qs2 = A.prefetch_related("books")
    
  1503.         with self.assertNumQueries(2, using="other"):
    
  1504.             authors = "".join(
    
  1505.                 "%s: %s\n"
    
  1506.                 % (author.name, ", ".join(b.title for b in author.books.all()))
    
  1507.                 for author in qs2
    
  1508.             )
    
  1509.         self.assertEqual(
    
  1510.             authors,
    
  1511.             "Charlotte: Poems, Jane Eyre\n"
    
  1512.             "Anne: Poems\n"
    
  1513.             "Emily: Poems, Wuthering Heights\n"
    
  1514.             "Jane: Sense and Sensibility\n",
    
  1515.         )
    
  1516. 
    
  1517.     def test_using_is_honored_fkey(self):
    
  1518.         B = Book.objects.using("other")
    
  1519.         A = Author.objects.using("other")
    
  1520.         book1 = B.create(title="Poems")
    
  1521.         book2 = B.create(title="Sense and Sensibility")
    
  1522. 
    
  1523.         A.create(name="Charlotte Bronte", first_book=book1)
    
  1524.         A.create(name="Jane Austen", first_book=book2)
    
  1525. 
    
  1526.         # Forward
    
  1527.         with self.assertNumQueries(2, using="other"):
    
  1528.             books = ", ".join(
    
  1529.                 a.first_book.title for a in A.prefetch_related("first_book")
    
  1530.             )
    
  1531.         self.assertEqual("Poems, Sense and Sensibility", books)
    
  1532. 
    
  1533.         # Reverse
    
  1534.         with self.assertNumQueries(2, using="other"):
    
  1535.             books = "".join(
    
  1536.                 "%s (%s)\n"
    
  1537.                 % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
    
  1538.                 for b in B.prefetch_related("first_time_authors")
    
  1539.             )
    
  1540.         self.assertEqual(
    
  1541.             books,
    
  1542.             "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
    
  1543.         )
    
  1544. 
    
  1545.     def test_using_is_honored_inheritance(self):
    
  1546.         B = BookWithYear.objects.using("other")
    
  1547.         A = AuthorWithAge.objects.using("other")
    
  1548.         book1 = B.create(title="Poems", published_year=2010)
    
  1549.         B.create(title="More poems", published_year=2011)
    
  1550.         A.create(name="Jane", first_book=book1, age=50)
    
  1551.         A.create(name="Tom", first_book=book1, age=49)
    
  1552. 
    
  1553.         # parent link
    
  1554.         with self.assertNumQueries(2, using="other"):
    
  1555.             authors = ", ".join(a.author.name for a in A.prefetch_related("author"))
    
  1556. 
    
  1557.         self.assertEqual(authors, "Jane, Tom")
    
  1558. 
    
  1559.         # child link
    
  1560.         with self.assertNumQueries(2, using="other"):
    
  1561.             ages = ", ".join(
    
  1562.                 str(a.authorwithage.age) for a in A.prefetch_related("authorwithage")
    
  1563.             )
    
  1564. 
    
  1565.         self.assertEqual(ages, "50, 49")
    
  1566. 
    
  1567.     def test_using_is_honored_custom_qs(self):
    
  1568.         B = Book.objects.using("other")
    
  1569.         A = Author.objects.using("other")
    
  1570.         book1 = B.create(title="Poems")
    
  1571.         book2 = B.create(title="Sense and Sensibility")
    
  1572. 
    
  1573.         A.create(name="Charlotte Bronte", first_book=book1)
    
  1574.         A.create(name="Jane Austen", first_book=book2)
    
  1575. 
    
  1576.         # Implicit hinting
    
  1577.         with self.assertNumQueries(2, using="other"):
    
  1578.             prefetch = Prefetch("first_time_authors", queryset=Author.objects.all())
    
  1579.             books = "".join(
    
  1580.                 "%s (%s)\n"
    
  1581.                 % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
    
  1582.                 for b in B.prefetch_related(prefetch)
    
  1583.             )
    
  1584.         self.assertEqual(
    
  1585.             books,
    
  1586.             "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
    
  1587.         )
    
  1588.         # Explicit using on the same db.
    
  1589.         with self.assertNumQueries(2, using="other"):
    
  1590.             prefetch = Prefetch(
    
  1591.                 "first_time_authors", queryset=Author.objects.using("other")
    
  1592.             )
    
  1593.             books = "".join(
    
  1594.                 "%s (%s)\n"
    
  1595.                 % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
    
  1596.                 for b in B.prefetch_related(prefetch)
    
  1597.             )
    
  1598.         self.assertEqual(
    
  1599.             books,
    
  1600.             "Poems (Charlotte Bronte)\nSense and Sensibility (Jane Austen)\n",
    
  1601.         )
    
  1602. 
    
  1603.         # Explicit using on a different db.
    
  1604.         with self.assertNumQueries(1, using="default"), self.assertNumQueries(
    
  1605.             1, using="other"
    
  1606.         ):
    
  1607.             prefetch = Prefetch(
    
  1608.                 "first_time_authors", queryset=Author.objects.using("default")
    
  1609.             )
    
  1610.             books = "".join(
    
  1611.                 "%s (%s)\n"
    
  1612.                 % (b.title, ", ".join(a.name for a in b.first_time_authors.all()))
    
  1613.                 for b in B.prefetch_related(prefetch)
    
  1614.             )
    
  1615.         self.assertEqual(books, "Poems ()\n" "Sense and Sensibility ()\n")
    
  1616. 
    
  1617. 
    
  1618. class Ticket19607Tests(TestCase):
    
  1619.     @classmethod
    
  1620.     def setUpTestData(cls):
    
  1621.         LessonEntry.objects.bulk_create(
    
  1622.             LessonEntry(id=id_, name1=name1, name2=name2)
    
  1623.             for id_, name1, name2 in [
    
  1624.                 (1, "einfach", "simple"),
    
  1625.                 (2, "schwierig", "difficult"),
    
  1626.             ]
    
  1627.         )
    
  1628.         WordEntry.objects.bulk_create(
    
  1629.             WordEntry(id=id_, lesson_entry_id=lesson_entry_id, name=name)
    
  1630.             for id_, lesson_entry_id, name in [
    
  1631.                 (1, 1, "einfach"),
    
  1632.                 (2, 1, "simple"),
    
  1633.                 (3, 2, "schwierig"),
    
  1634.                 (4, 2, "difficult"),
    
  1635.             ]
    
  1636.         )
    
  1637. 
    
  1638.     def test_bug(self):
    
  1639.         list(
    
  1640.             WordEntry.objects.prefetch_related(
    
  1641.                 "lesson_entry", "lesson_entry__wordentry_set"
    
  1642.             )
    
  1643.         )
    
  1644. 
    
  1645. 
    
  1646. class Ticket21410Tests(TestCase):
    
  1647.     @classmethod
    
  1648.     def setUpTestData(cls):
    
  1649.         book1 = Book.objects.create(title="Poems")
    
  1650.         book2 = Book.objects.create(title="Jane Eyre")
    
  1651.         book3 = Book.objects.create(title="Wuthering Heights")
    
  1652.         book4 = Book.objects.create(title="Sense and Sensibility")
    
  1653. 
    
  1654.         author1 = Author2.objects.create(name="Charlotte", first_book=book1)
    
  1655.         author2 = Author2.objects.create(name="Anne", first_book=book1)
    
  1656.         author3 = Author2.objects.create(name="Emily", first_book=book1)
    
  1657.         author4 = Author2.objects.create(name="Jane", first_book=book4)
    
  1658. 
    
  1659.         author1.favorite_books.add(book1, book2, book3)
    
  1660.         author2.favorite_books.add(book1)
    
  1661.         author3.favorite_books.add(book2)
    
  1662.         author4.favorite_books.add(book3)
    
  1663. 
    
  1664.     def test_bug(self):
    
  1665.         list(Author2.objects.prefetch_related("first_book", "favorite_books"))
    
  1666. 
    
  1667. 
    
  1668. class Ticket21760Tests(TestCase):
    
  1669.     @classmethod
    
  1670.     def setUpTestData(cls):
    
  1671.         cls.rooms = []
    
  1672.         for _ in range(3):
    
  1673.             house = House.objects.create()
    
  1674.             for _ in range(3):
    
  1675.                 cls.rooms.append(Room.objects.create(house=house))
    
  1676.             # Set main_room for each house before creating the next one for
    
  1677.             # databases where supports_nullable_unique_constraints is False.
    
  1678.             house.main_room = cls.rooms[-3]
    
  1679.             house.save()
    
  1680. 
    
  1681.     def test_bug(self):
    
  1682.         prefetcher = get_prefetcher(self.rooms[0], "house", "house")[0]
    
  1683.         queryset = prefetcher.get_prefetch_queryset(list(Room.objects.all()))[0]
    
  1684.         self.assertNotIn(" JOIN ", str(queryset.query))
    
  1685. 
    
  1686. 
    
  1687. class DirectPrefetchedObjectCacheReuseTests(TestCase):
    
  1688.     """
    
  1689.     prefetch_related() reuses objects fetched in _prefetched_objects_cache.
    
  1690. 
    
  1691.     When objects are prefetched and not stored as an instance attribute (often
    
  1692.     intermediary relationships), they are saved to the
    
  1693.     _prefetched_objects_cache attribute. prefetch_related() takes
    
  1694.     _prefetched_objects_cache into account when determining whether an object
    
  1695.     has been fetched[1] and retrieves results from it when it is populated [2].
    
  1696. 
    
  1697.     [1]: #25546 (duplicate queries on nested Prefetch)
    
  1698.     [2]: #27554 (queryset evaluation fails with a mix of nested and flattened
    
  1699.         prefetches)
    
  1700.     """
    
  1701. 
    
  1702.     @classmethod
    
  1703.     def setUpTestData(cls):
    
  1704.         cls.book1, cls.book2 = [
    
  1705.             Book.objects.create(title="book1"),
    
  1706.             Book.objects.create(title="book2"),
    
  1707.         ]
    
  1708.         cls.author11, cls.author12, cls.author21 = [
    
  1709.             Author.objects.create(first_book=cls.book1, name="Author11"),
    
  1710.             Author.objects.create(first_book=cls.book1, name="Author12"),
    
  1711.             Author.objects.create(first_book=cls.book2, name="Author21"),
    
  1712.         ]
    
  1713.         cls.author1_address1, cls.author1_address2, cls.author2_address1 = [
    
  1714.             AuthorAddress.objects.create(author=cls.author11, address="Happy place"),
    
  1715.             AuthorAddress.objects.create(author=cls.author12, address="Haunted house"),
    
  1716.             AuthorAddress.objects.create(author=cls.author21, address="Happy place"),
    
  1717.         ]
    
  1718.         cls.bookwithyear1 = BookWithYear.objects.create(
    
  1719.             title="Poems", published_year=2010
    
  1720.         )
    
  1721.         cls.bookreview1 = BookReview.objects.create(book=cls.bookwithyear1)
    
  1722. 
    
  1723.     def test_detect_is_fetched(self):
    
  1724.         """
    
  1725.         Nested prefetch_related() shouldn't trigger duplicate queries for the same
    
  1726.         lookup.
    
  1727.         """
    
  1728.         with self.assertNumQueries(3):
    
  1729.             books = Book.objects.filter(
    
  1730.                 title__in=["book1", "book2"],
    
  1731.             ).prefetch_related(
    
  1732.                 Prefetch(
    
  1733.                     "first_time_authors",
    
  1734.                     Author.objects.prefetch_related(
    
  1735.                         Prefetch(
    
  1736.                             "addresses",
    
  1737.                             AuthorAddress.objects.filter(address="Happy place"),
    
  1738.                         )
    
  1739.                     ),
    
  1740.                 ),
    
  1741.             )
    
  1742.             book1, book2 = list(books)
    
  1743. 
    
  1744.         with self.assertNumQueries(0):
    
  1745.             self.assertSequenceEqual(
    
  1746.                 book1.first_time_authors.all(), [self.author11, self.author12]
    
  1747.             )
    
  1748.             self.assertSequenceEqual(book2.first_time_authors.all(), [self.author21])
    
  1749. 
    
  1750.             self.assertSequenceEqual(
    
  1751.                 book1.first_time_authors.all()[0].addresses.all(),
    
  1752.                 [self.author1_address1],
    
  1753.             )
    
  1754.             self.assertSequenceEqual(
    
  1755.                 book1.first_time_authors.all()[1].addresses.all(), []
    
  1756.             )
    
  1757.             self.assertSequenceEqual(
    
  1758.                 book2.first_time_authors.all()[0].addresses.all(),
    
  1759.                 [self.author2_address1],
    
  1760.             )
    
  1761. 
    
  1762.         self.assertEqual(
    
  1763.             list(book1.first_time_authors.all()),
    
  1764.             list(book1.first_time_authors.all().all()),
    
  1765.         )
    
  1766.         self.assertEqual(
    
  1767.             list(book2.first_time_authors.all()),
    
  1768.             list(book2.first_time_authors.all().all()),
    
  1769.         )
    
  1770.         self.assertEqual(
    
  1771.             list(book1.first_time_authors.all()[0].addresses.all()),
    
  1772.             list(book1.first_time_authors.all()[0].addresses.all().all()),
    
  1773.         )
    
  1774.         self.assertEqual(
    
  1775.             list(book1.first_time_authors.all()[1].addresses.all()),
    
  1776.             list(book1.first_time_authors.all()[1].addresses.all().all()),
    
  1777.         )
    
  1778.         self.assertEqual(
    
  1779.             list(book2.first_time_authors.all()[0].addresses.all()),
    
  1780.             list(book2.first_time_authors.all()[0].addresses.all().all()),
    
  1781.         )
    
  1782. 
    
  1783.     def test_detect_is_fetched_with_to_attr(self):
    
  1784.         with self.assertNumQueries(3):
    
  1785.             books = Book.objects.filter(
    
  1786.                 title__in=["book1", "book2"],
    
  1787.             ).prefetch_related(
    
  1788.                 Prefetch(
    
  1789.                     "first_time_authors",
    
  1790.                     Author.objects.prefetch_related(
    
  1791.                         Prefetch(
    
  1792.                             "addresses",
    
  1793.                             AuthorAddress.objects.filter(address="Happy place"),
    
  1794.                             to_attr="happy_place",
    
  1795.                         )
    
  1796.                     ),
    
  1797.                     to_attr="first_authors",
    
  1798.                 ),
    
  1799.             )
    
  1800.             book1, book2 = list(books)
    
  1801. 
    
  1802.         with self.assertNumQueries(0):
    
  1803.             self.assertEqual(book1.first_authors, [self.author11, self.author12])
    
  1804.             self.assertEqual(book2.first_authors, [self.author21])
    
  1805. 
    
  1806.             self.assertEqual(
    
  1807.                 book1.first_authors[0].happy_place, [self.author1_address1]
    
  1808.             )
    
  1809.             self.assertEqual(book1.first_authors[1].happy_place, [])
    
  1810.             self.assertEqual(
    
  1811.                 book2.first_authors[0].happy_place, [self.author2_address1]
    
  1812.             )
    
  1813. 
    
  1814.     def test_prefetch_reverse_foreign_key(self):
    
  1815.         with self.assertNumQueries(2):
    
  1816.             (bookwithyear1,) = BookWithYear.objects.prefetch_related("bookreview_set")
    
  1817.         with self.assertNumQueries(0):
    
  1818.             self.assertCountEqual(
    
  1819.                 bookwithyear1.bookreview_set.all(), [self.bookreview1]
    
  1820.             )
    
  1821.         with self.assertNumQueries(0):
    
  1822.             prefetch_related_objects([bookwithyear1], "bookreview_set")
    
  1823. 
    
  1824.     def test_add_clears_prefetched_objects(self):
    
  1825.         bookwithyear = BookWithYear.objects.get(pk=self.bookwithyear1.pk)
    
  1826.         prefetch_related_objects([bookwithyear], "bookreview_set")
    
  1827.         self.assertCountEqual(bookwithyear.bookreview_set.all(), [self.bookreview1])
    
  1828.         new_review = BookReview.objects.create()
    
  1829.         bookwithyear.bookreview_set.add(new_review)
    
  1830.         self.assertCountEqual(
    
  1831.             bookwithyear.bookreview_set.all(), [self.bookreview1, new_review]
    
  1832.         )
    
  1833. 
    
  1834.     def test_remove_clears_prefetched_objects(self):
    
  1835.         bookwithyear = BookWithYear.objects.get(pk=self.bookwithyear1.pk)
    
  1836.         prefetch_related_objects([bookwithyear], "bookreview_set")
    
  1837.         self.assertCountEqual(bookwithyear.bookreview_set.all(), [self.bookreview1])
    
  1838.         bookwithyear.bookreview_set.remove(self.bookreview1)
    
  1839.         self.assertCountEqual(bookwithyear.bookreview_set.all(), [])
    
  1840. 
    
  1841. 
    
  1842. class ReadPrefetchedObjectsCacheTests(TestCase):
    
  1843.     @classmethod
    
  1844.     def setUpTestData(cls):
    
  1845.         cls.book1 = Book.objects.create(title="Les confessions Volume I")
    
  1846.         cls.book2 = Book.objects.create(title="Candide")
    
  1847.         cls.author1 = AuthorWithAge.objects.create(
    
  1848.             name="Rousseau", first_book=cls.book1, age=70
    
  1849.         )
    
  1850.         cls.author2 = AuthorWithAge.objects.create(
    
  1851.             name="Voltaire", first_book=cls.book2, age=65
    
  1852.         )
    
  1853.         cls.book1.authors.add(cls.author1)
    
  1854.         cls.book2.authors.add(cls.author2)
    
  1855.         FavoriteAuthors.objects.create(author=cls.author1, likes_author=cls.author2)
    
  1856. 
    
  1857.     def test_retrieves_results_from_prefetched_objects_cache(self):
    
  1858.         """
    
  1859.         When intermediary results are prefetched without a destination
    
  1860.         attribute, they are saved in the RelatedManager's cache
    
  1861.         (_prefetched_objects_cache). prefetch_related() uses this cache
    
  1862.         (#27554).
    
  1863.         """
    
  1864.         authors = AuthorWithAge.objects.prefetch_related(
    
  1865.             Prefetch(
    
  1866.                 "author",
    
  1867.                 queryset=Author.objects.prefetch_related(
    
  1868.                     # Results are saved in the RelatedManager's cache
    
  1869.                     # (_prefetched_objects_cache) and do not replace the
    
  1870.                     # RelatedManager on Author instances (favorite_authors)
    
  1871.                     Prefetch("favorite_authors__first_book"),
    
  1872.                 ),
    
  1873.             ),
    
  1874.         )
    
  1875.         with self.assertNumQueries(4):
    
  1876.             # AuthorWithAge -> Author -> FavoriteAuthors, Book
    
  1877.             self.assertSequenceEqual(authors, [self.author1, self.author2])
    
  1878. 
    
  1879. 
    
  1880. class NestedPrefetchTests(TestCase):
    
  1881.     @classmethod
    
  1882.     def setUpTestData(cls):
    
  1883.         house = House.objects.create(name="Big house", address="123 Main St")
    
  1884.         cls.room = Room.objects.create(name="Kitchen", house=house)
    
  1885. 
    
  1886.     def test_nested_prefetch_is_not_overwritten_by_related_object(self):
    
  1887.         """
    
  1888.         The prefetched relationship is used rather than populating the reverse
    
  1889.         relationship from the parent, when prefetching a set of child objects
    
  1890.         related to a set of parent objects and the child queryset itself
    
  1891.         specifies a prefetch back to the parent.
    
  1892.         """
    
  1893.         queryset = House.objects.only("name").prefetch_related(
    
  1894.             Prefetch(
    
  1895.                 "rooms",
    
  1896.                 queryset=Room.objects.prefetch_related(
    
  1897.                     Prefetch("house", queryset=House.objects.only("address")),
    
  1898.                 ),
    
  1899.             ),
    
  1900.         )
    
  1901.         with self.assertNumQueries(3):
    
  1902.             house = queryset.first()
    
  1903. 
    
  1904.         self.assertIs(Room.house.is_cached(self.room), True)
    
  1905.         with self.assertNumQueries(0):
    
  1906.             house.rooms.first().house.address