1. from django.core.exceptions import FieldError
    
  2. from django.db.models import FilteredRelation
    
  3. from django.test import SimpleTestCase, TestCase
    
  4. 
    
  5. from .models import (
    
  6.     AdvancedUserStat,
    
  7.     Child1,
    
  8.     Child2,
    
  9.     Child3,
    
  10.     Child4,
    
  11.     Image,
    
  12.     LinkedList,
    
  13.     Parent1,
    
  14.     Parent2,
    
  15.     Product,
    
  16.     StatDetails,
    
  17.     User,
    
  18.     UserProfile,
    
  19.     UserStat,
    
  20.     UserStatResult,
    
  21. )
    
  22. 
    
  23. 
    
  24. class ReverseSelectRelatedTestCase(TestCase):
    
  25.     @classmethod
    
  26.     def setUpTestData(cls):
    
  27.         user = User.objects.create(username="test")
    
  28.         UserProfile.objects.create(user=user, state="KS", city="Lawrence")
    
  29.         results = UserStatResult.objects.create(results="first results")
    
  30.         userstat = UserStat.objects.create(user=user, posts=150, results=results)
    
  31.         StatDetails.objects.create(base_stats=userstat, comments=259)
    
  32. 
    
  33.         user2 = User.objects.create(username="bob")
    
  34.         results2 = UserStatResult.objects.create(results="moar results")
    
  35.         advstat = AdvancedUserStat.objects.create(
    
  36.             user=user2, posts=200, karma=5, results=results2
    
  37.         )
    
  38.         StatDetails.objects.create(base_stats=advstat, comments=250)
    
  39.         p1 = Parent1(name1="Only Parent1")
    
  40.         p1.save()
    
  41.         c1 = Child1(name1="Child1 Parent1", name2="Child1 Parent2", value=1)
    
  42.         c1.save()
    
  43.         p2 = Parent2(name2="Child2 Parent2")
    
  44.         p2.save()
    
  45.         c2 = Child2(name1="Child2 Parent1", parent2=p2, value=2)
    
  46.         c2.save()
    
  47. 
    
  48.     def test_basic(self):
    
  49.         with self.assertNumQueries(1):
    
  50.             u = User.objects.select_related("userprofile").get(username="test")
    
  51.             self.assertEqual(u.userprofile.state, "KS")
    
  52. 
    
  53.     def test_follow_next_level(self):
    
  54.         with self.assertNumQueries(1):
    
  55.             u = User.objects.select_related("userstat__results").get(username="test")
    
  56.             self.assertEqual(u.userstat.posts, 150)
    
  57.             self.assertEqual(u.userstat.results.results, "first results")
    
  58. 
    
  59.     def test_follow_two(self):
    
  60.         with self.assertNumQueries(1):
    
  61.             u = User.objects.select_related("userprofile", "userstat").get(
    
  62.                 username="test"
    
  63.             )
    
  64.             self.assertEqual(u.userprofile.state, "KS")
    
  65.             self.assertEqual(u.userstat.posts, 150)
    
  66. 
    
  67.     def test_follow_two_next_level(self):
    
  68.         with self.assertNumQueries(1):
    
  69.             u = User.objects.select_related(
    
  70.                 "userstat__results", "userstat__statdetails"
    
  71.             ).get(username="test")
    
  72.             self.assertEqual(u.userstat.results.results, "first results")
    
  73.             self.assertEqual(u.userstat.statdetails.comments, 259)
    
  74. 
    
  75.     def test_forward_and_back(self):
    
  76.         with self.assertNumQueries(1):
    
  77.             stat = UserStat.objects.select_related("user__userprofile").get(
    
  78.                 user__username="test"
    
  79.             )
    
  80.             self.assertEqual(stat.user.userprofile.state, "KS")
    
  81.             self.assertEqual(stat.user.userstat.posts, 150)
    
  82. 
    
  83.     def test_back_and_forward(self):
    
  84.         with self.assertNumQueries(1):
    
  85.             u = User.objects.select_related("userstat").get(username="test")
    
  86.             self.assertEqual(u.userstat.user.username, "test")
    
  87. 
    
  88.     def test_not_followed_by_default(self):
    
  89.         with self.assertNumQueries(2):
    
  90.             u = User.objects.select_related().get(username="test")
    
  91.             self.assertEqual(u.userstat.posts, 150)
    
  92. 
    
  93.     def test_follow_from_child_class(self):
    
  94.         with self.assertNumQueries(1):
    
  95.             stat = AdvancedUserStat.objects.select_related("user", "statdetails").get(
    
  96.                 posts=200
    
  97.             )
    
  98.             self.assertEqual(stat.statdetails.comments, 250)
    
  99.             self.assertEqual(stat.user.username, "bob")
    
  100. 
    
  101.     def test_follow_inheritance(self):
    
  102.         with self.assertNumQueries(1):
    
  103.             stat = UserStat.objects.select_related("user", "advanceduserstat").get(
    
  104.                 posts=200
    
  105.             )
    
  106.             self.assertEqual(stat.advanceduserstat.posts, 200)
    
  107.             self.assertEqual(stat.user.username, "bob")
    
  108.         with self.assertNumQueries(0):
    
  109.             self.assertEqual(stat.advanceduserstat.user.username, "bob")
    
  110. 
    
  111.     def test_nullable_relation(self):
    
  112.         im = Image.objects.create(name="imag1")
    
  113.         p1 = Product.objects.create(name="Django Plushie", image=im)
    
  114.         p2 = Product.objects.create(name="Talking Django Plushie")
    
  115. 
    
  116.         with self.assertNumQueries(1):
    
  117.             result = sorted(
    
  118.                 Product.objects.select_related("image"), key=lambda x: x.name
    
  119.             )
    
  120.             self.assertEqual(
    
  121.                 [p.name for p in result], ["Django Plushie", "Talking Django Plushie"]
    
  122.             )
    
  123. 
    
  124.             self.assertEqual(p1.image, im)
    
  125.             # Check for ticket #13839
    
  126.             self.assertIsNone(p2.image)
    
  127. 
    
  128.     def test_missing_reverse(self):
    
  129.         """
    
  130.         Ticket #13839: select_related() should NOT cache None
    
  131.         for missing objects on a reverse 1-1 relation.
    
  132.         """
    
  133.         with self.assertNumQueries(1):
    
  134.             user = User.objects.select_related("userprofile").get(username="bob")
    
  135.             with self.assertRaises(UserProfile.DoesNotExist):
    
  136.                 user.userprofile
    
  137. 
    
  138.     def test_nullable_missing_reverse(self):
    
  139.         """
    
  140.         Ticket #13839: select_related() should NOT cache None
    
  141.         for missing objects on a reverse 0-1 relation.
    
  142.         """
    
  143.         Image.objects.create(name="imag1")
    
  144. 
    
  145.         with self.assertNumQueries(1):
    
  146.             image = Image.objects.select_related("product").get()
    
  147.             with self.assertRaises(Product.DoesNotExist):
    
  148.                 image.product
    
  149. 
    
  150.     def test_parent_only(self):
    
  151.         with self.assertNumQueries(1):
    
  152.             p = Parent1.objects.select_related("child1").get(name1="Only Parent1")
    
  153.         with self.assertNumQueries(0):
    
  154.             with self.assertRaises(Child1.DoesNotExist):
    
  155.                 p.child1
    
  156. 
    
  157.     def test_multiple_subclass(self):
    
  158.         with self.assertNumQueries(1):
    
  159.             p = Parent1.objects.select_related("child1").get(name1="Child1 Parent1")
    
  160.             self.assertEqual(p.child1.name2, "Child1 Parent2")
    
  161. 
    
  162.     def test_onetoone_with_subclass(self):
    
  163.         with self.assertNumQueries(1):
    
  164.             p = Parent2.objects.select_related("child2").get(name2="Child2 Parent2")
    
  165.             self.assertEqual(p.child2.name1, "Child2 Parent1")
    
  166. 
    
  167.     def test_onetoone_with_two_subclasses(self):
    
  168.         with self.assertNumQueries(1):
    
  169.             p = Parent2.objects.select_related("child2", "child2__child3").get(
    
  170.                 name2="Child2 Parent2"
    
  171.             )
    
  172.             self.assertEqual(p.child2.name1, "Child2 Parent1")
    
  173.             with self.assertRaises(Child3.DoesNotExist):
    
  174.                 p.child2.child3
    
  175.         p3 = Parent2(name2="Child3 Parent2")
    
  176.         p3.save()
    
  177.         c2 = Child3(name1="Child3 Parent1", parent2=p3, value=2, value3=3)
    
  178.         c2.save()
    
  179.         with self.assertNumQueries(1):
    
  180.             p = Parent2.objects.select_related("child2", "child2__child3").get(
    
  181.                 name2="Child3 Parent2"
    
  182.             )
    
  183.             self.assertEqual(p.child2.name1, "Child3 Parent1")
    
  184.             self.assertEqual(p.child2.child3.value3, 3)
    
  185.             self.assertEqual(p.child2.child3.value, p.child2.value)
    
  186.             self.assertEqual(p.child2.name1, p.child2.child3.name1)
    
  187. 
    
  188.     def test_multiinheritance_two_subclasses(self):
    
  189.         with self.assertNumQueries(1):
    
  190.             p = Parent1.objects.select_related("child1", "child1__child4").get(
    
  191.                 name1="Child1 Parent1"
    
  192.             )
    
  193.             self.assertEqual(p.child1.name2, "Child1 Parent2")
    
  194.             self.assertEqual(p.child1.name1, p.name1)
    
  195.             with self.assertRaises(Child4.DoesNotExist):
    
  196.                 p.child1.child4
    
  197.         Child4(name1="n1", name2="n2", value=1, value4=4).save()
    
  198.         with self.assertNumQueries(1):
    
  199.             p = Parent2.objects.select_related("child1", "child1__child4").get(
    
  200.                 name2="n2"
    
  201.             )
    
  202.             self.assertEqual(p.name2, "n2")
    
  203.             self.assertEqual(p.child1.name1, "n1")
    
  204.             self.assertEqual(p.child1.name2, p.name2)
    
  205.             self.assertEqual(p.child1.value, 1)
    
  206.             self.assertEqual(p.child1.child4.name1, p.child1.name1)
    
  207.             self.assertEqual(p.child1.child4.name2, p.child1.name2)
    
  208.             self.assertEqual(p.child1.child4.value, p.child1.value)
    
  209.             self.assertEqual(p.child1.child4.value4, 4)
    
  210. 
    
  211.     def test_inheritance_deferred(self):
    
  212.         c = Child4.objects.create(name1="n1", name2="n2", value=1, value4=4)
    
  213.         with self.assertNumQueries(1):
    
  214.             p = (
    
  215.                 Parent2.objects.select_related("child1")
    
  216.                 .only("id2", "child1__value")
    
  217.                 .get(name2="n2")
    
  218.             )
    
  219.             self.assertEqual(p.id2, c.id2)
    
  220.             self.assertEqual(p.child1.value, 1)
    
  221.         p = (
    
  222.             Parent2.objects.select_related("child1")
    
  223.             .only("id2", "child1__value")
    
  224.             .get(name2="n2")
    
  225.         )
    
  226.         with self.assertNumQueries(1):
    
  227.             self.assertEqual(p.name2, "n2")
    
  228.         p = (
    
  229.             Parent2.objects.select_related("child1")
    
  230.             .only("id2", "child1__value")
    
  231.             .get(name2="n2")
    
  232.         )
    
  233.         with self.assertNumQueries(1):
    
  234.             self.assertEqual(p.child1.name2, "n2")
    
  235. 
    
  236.     def test_inheritance_deferred2(self):
    
  237.         c = Child4.objects.create(name1="n1", name2="n2", value=1, value4=4)
    
  238.         qs = Parent2.objects.select_related("child1", "child1__child4").only(
    
  239.             "id2", "child1__value", "child1__child4__value4"
    
  240.         )
    
  241.         with self.assertNumQueries(1):
    
  242.             p = qs.get(name2="n2")
    
  243.             self.assertEqual(p.id2, c.id2)
    
  244.             self.assertEqual(p.child1.value, 1)
    
  245.             self.assertEqual(p.child1.child4.value4, 4)
    
  246.             self.assertEqual(p.child1.child4.id2, c.id2)
    
  247.         p = qs.get(name2="n2")
    
  248.         with self.assertNumQueries(1):
    
  249.             self.assertEqual(p.child1.name2, "n2")
    
  250.         p = qs.get(name2="n2")
    
  251.         with self.assertNumQueries(0):
    
  252.             self.assertEqual(p.child1.name1, "n1")
    
  253.             self.assertEqual(p.child1.child4.name1, "n1")
    
  254. 
    
  255.     def test_self_relation(self):
    
  256.         item1 = LinkedList.objects.create(name="item1")
    
  257.         LinkedList.objects.create(name="item2", previous_item=item1)
    
  258.         with self.assertNumQueries(1):
    
  259.             item1_db = LinkedList.objects.select_related("next_item").get(name="item1")
    
  260.             self.assertEqual(item1_db.next_item.name, "item2")
    
  261. 
    
  262. 
    
  263. class ReverseSelectRelatedValidationTests(SimpleTestCase):
    
  264.     """
    
  265.     Rverse related fields should be listed in the validation message when an
    
  266.     invalid field is given in select_related().
    
  267.     """
    
  268. 
    
  269.     non_relational_error = (
    
  270.         "Non-relational field given in select_related: '%s'. Choices are: %s"
    
  271.     )
    
  272.     invalid_error = (
    
  273.         "Invalid field name(s) given in select_related: '%s'. Choices are: %s"
    
  274.     )
    
  275. 
    
  276.     def test_reverse_related_validation(self):
    
  277.         fields = "userprofile, userstat"
    
  278. 
    
  279.         with self.assertRaisesMessage(
    
  280.             FieldError, self.invalid_error % ("foobar", fields)
    
  281.         ):
    
  282.             list(User.objects.select_related("foobar"))
    
  283. 
    
  284.         with self.assertRaisesMessage(
    
  285.             FieldError, self.non_relational_error % ("username", fields)
    
  286.         ):
    
  287.             list(User.objects.select_related("username"))
    
  288. 
    
  289.     def test_reverse_related_validation_with_filtered_relation(self):
    
  290.         fields = "userprofile, userstat, relation"
    
  291.         with self.assertRaisesMessage(
    
  292.             FieldError, self.invalid_error % ("foobar", fields)
    
  293.         ):
    
  294.             list(
    
  295.                 User.objects.annotate(
    
  296.                     relation=FilteredRelation("userprofile")
    
  297.                 ).select_related("foobar")
    
  298.             )