1. from django.db.models import Prefetch, prefetch_related_objects
    
  2. from django.test import TestCase
    
  3. 
    
  4. from .models import Author, Book, Reader
    
  5. 
    
  6. 
    
  7. class PrefetchRelatedObjectsTests(TestCase):
    
  8.     """
    
  9.     Since prefetch_related_objects() is just the inner part of
    
  10.     prefetch_related(), only do basic tests to ensure its API hasn't changed.
    
  11.     """
    
  12. 
    
  13.     @classmethod
    
  14.     def setUpTestData(cls):
    
  15.         cls.book1 = Book.objects.create(title="Poems")
    
  16.         cls.book2 = Book.objects.create(title="Jane Eyre")
    
  17.         cls.book3 = Book.objects.create(title="Wuthering Heights")
    
  18.         cls.book4 = Book.objects.create(title="Sense and Sensibility")
    
  19. 
    
  20.         cls.author1 = Author.objects.create(name="Charlotte", first_book=cls.book1)
    
  21.         cls.author2 = Author.objects.create(name="Anne", first_book=cls.book1)
    
  22.         cls.author3 = Author.objects.create(name="Emily", first_book=cls.book1)
    
  23.         cls.author4 = Author.objects.create(name="Jane", first_book=cls.book4)
    
  24. 
    
  25.         cls.book1.authors.add(cls.author1, cls.author2, cls.author3)
    
  26.         cls.book2.authors.add(cls.author1)
    
  27.         cls.book3.authors.add(cls.author3)
    
  28.         cls.book4.authors.add(cls.author4)
    
  29. 
    
  30.         cls.reader1 = Reader.objects.create(name="Amy")
    
  31.         cls.reader2 = Reader.objects.create(name="Belinda")
    
  32. 
    
  33.         cls.reader1.books_read.add(cls.book1, cls.book4)
    
  34.         cls.reader2.books_read.add(cls.book2, cls.book4)
    
  35. 
    
  36.     def test_unknown(self):
    
  37.         book1 = Book.objects.get(id=self.book1.id)
    
  38.         with self.assertRaises(AttributeError):
    
  39.             prefetch_related_objects([book1], "unknown_attribute")
    
  40. 
    
  41.     def test_m2m_forward(self):
    
  42.         book1 = Book.objects.get(id=self.book1.id)
    
  43.         with self.assertNumQueries(1):
    
  44.             prefetch_related_objects([book1], "authors")
    
  45. 
    
  46.         with self.assertNumQueries(0):
    
  47.             self.assertCountEqual(
    
  48.                 book1.authors.all(), [self.author1, self.author2, self.author3]
    
  49.             )
    
  50. 
    
  51.     def test_m2m_reverse(self):
    
  52.         author1 = Author.objects.get(id=self.author1.id)
    
  53.         with self.assertNumQueries(1):
    
  54.             prefetch_related_objects([author1], "books")
    
  55. 
    
  56.         with self.assertNumQueries(0):
    
  57.             self.assertCountEqual(author1.books.all(), [self.book1, self.book2])
    
  58. 
    
  59.     def test_foreignkey_forward(self):
    
  60.         authors = list(Author.objects.all())
    
  61.         with self.assertNumQueries(1):
    
  62.             prefetch_related_objects(authors, "first_book")
    
  63. 
    
  64.         with self.assertNumQueries(0):
    
  65.             [author.first_book for author in authors]
    
  66. 
    
  67.     def test_foreignkey_reverse(self):
    
  68.         books = list(Book.objects.all())
    
  69.         with self.assertNumQueries(1):
    
  70.             prefetch_related_objects(books, "first_time_authors")
    
  71. 
    
  72.         with self.assertNumQueries(0):
    
  73.             [list(book.first_time_authors.all()) for book in books]
    
  74. 
    
  75.     def test_m2m_then_m2m(self):
    
  76.         """A m2m can be followed through another m2m."""
    
  77.         authors = list(Author.objects.all())
    
  78.         with self.assertNumQueries(2):
    
  79.             prefetch_related_objects(authors, "books__read_by")
    
  80. 
    
  81.         with self.assertNumQueries(0):
    
  82.             self.assertEqual(
    
  83.                 [
    
  84.                     [[str(r) for r in b.read_by.all()] for b in a.books.all()]
    
  85.                     for a in authors
    
  86.                 ],
    
  87.                 [
    
  88.                     [["Amy"], ["Belinda"]],  # Charlotte - Poems, Jane Eyre
    
  89.                     [["Amy"]],  # Anne - Poems
    
  90.                     [["Amy"], []],  # Emily - Poems, Wuthering Heights
    
  91.                     [["Amy", "Belinda"]],  # Jane - Sense and Sense
    
  92.                 ],
    
  93.             )
    
  94. 
    
  95.     def test_prefetch_object(self):
    
  96.         book1 = Book.objects.get(id=self.book1.id)
    
  97.         with self.assertNumQueries(1):
    
  98.             prefetch_related_objects([book1], Prefetch("authors"))
    
  99. 
    
  100.         with self.assertNumQueries(0):
    
  101.             self.assertCountEqual(
    
  102.                 book1.authors.all(), [self.author1, self.author2, self.author3]
    
  103.             )
    
  104. 
    
  105.     def test_prefetch_object_twice(self):
    
  106.         book1 = Book.objects.get(id=self.book1.id)
    
  107.         book2 = Book.objects.get(id=self.book2.id)
    
  108.         with self.assertNumQueries(1):
    
  109.             prefetch_related_objects([book1], Prefetch("authors"))
    
  110.         with self.assertNumQueries(1):
    
  111.             prefetch_related_objects([book1, book2], Prefetch("authors"))
    
  112.         with self.assertNumQueries(0):
    
  113.             self.assertCountEqual(book2.authors.all(), [self.author1])
    
  114. 
    
  115.     def test_prefetch_object_to_attr(self):
    
  116.         book1 = Book.objects.get(id=self.book1.id)
    
  117.         with self.assertNumQueries(1):
    
  118.             prefetch_related_objects(
    
  119.                 [book1], Prefetch("authors", to_attr="the_authors")
    
  120.             )
    
  121. 
    
  122.         with self.assertNumQueries(0):
    
  123.             self.assertCountEqual(
    
  124.                 book1.the_authors, [self.author1, self.author2, self.author3]
    
  125.             )
    
  126. 
    
  127.     def test_prefetch_object_to_attr_twice(self):
    
  128.         book1 = Book.objects.get(id=self.book1.id)
    
  129.         book2 = Book.objects.get(id=self.book2.id)
    
  130.         with self.assertNumQueries(1):
    
  131.             prefetch_related_objects(
    
  132.                 [book1],
    
  133.                 Prefetch("authors", to_attr="the_authors"),
    
  134.             )
    
  135.         with self.assertNumQueries(1):
    
  136.             prefetch_related_objects(
    
  137.                 [book1, book2],
    
  138.                 Prefetch("authors", to_attr="the_authors"),
    
  139.             )
    
  140.         with self.assertNumQueries(0):
    
  141.             self.assertCountEqual(book2.the_authors, [self.author1])
    
  142. 
    
  143.     def test_prefetch_queryset(self):
    
  144.         book1 = Book.objects.get(id=self.book1.id)
    
  145.         with self.assertNumQueries(1):
    
  146.             prefetch_related_objects(
    
  147.                 [book1],
    
  148.                 Prefetch(
    
  149.                     "authors",
    
  150.                     queryset=Author.objects.filter(
    
  151.                         id__in=[self.author1.id, self.author2.id]
    
  152.                     ),
    
  153.                 ),
    
  154.             )
    
  155. 
    
  156.         with self.assertNumQueries(0):
    
  157.             self.assertCountEqual(book1.authors.all(), [self.author1, self.author2])