1. from django.test import TestCase
    
  2. 
    
  3. from .models import (
    
  4.     Event,
    
  5.     Movie,
    
  6.     Package,
    
  7.     PackageNullFK,
    
  8.     Person,
    
  9.     Screening,
    
  10.     ScreeningNullFK,
    
  11. )
    
  12. 
    
  13. 
    
  14. # These are tests for #16715. The basic scheme is always the same: 3 models with
    
  15. # 2 relations. The first relation may be null, while the second is non-nullable.
    
  16. # In some cases, Django would pick the wrong join type for the second relation,
    
  17. # resulting in missing objects in the queryset.
    
  18. #
    
  19. #   Model A
    
  20. #   | (Relation A/B : nullable)
    
  21. #   Model B
    
  22. #   | (Relation B/C : non-nullable)
    
  23. #   Model C
    
  24. #
    
  25. # Because of the possibility of NULL rows resulting from the LEFT OUTER JOIN
    
  26. # between Model A and Model B (i.e. instances of A without reference to B),
    
  27. # the second join must also be LEFT OUTER JOIN, so that we do not ignore
    
  28. # instances of A that do not reference B.
    
  29. #
    
  30. # Relation A/B can either be an explicit foreign key or an implicit reverse
    
  31. # relation such as introduced by one-to-one relations (through multi-table
    
  32. # inheritance).
    
  33. class NestedForeignKeysTests(TestCase):
    
  34.     @classmethod
    
  35.     def setUpTestData(cls):
    
  36.         cls.director = Person.objects.create(name="Terry Gilliam / Terry Jones")
    
  37.         cls.movie = Movie.objects.create(
    
  38.             title="Monty Python and the Holy Grail", director=cls.director
    
  39.         )
    
  40. 
    
  41.     # This test failed in #16715 because in some cases INNER JOIN was selected
    
  42.     # for the second foreign key relation instead of LEFT OUTER JOIN.
    
  43.     def test_inheritance(self):
    
  44.         Event.objects.create()
    
  45.         Screening.objects.create(movie=self.movie)
    
  46. 
    
  47.         self.assertEqual(len(Event.objects.all()), 2)
    
  48.         self.assertEqual(len(Event.objects.select_related("screening")), 2)
    
  49.         # This failed.
    
  50.         self.assertEqual(len(Event.objects.select_related("screening__movie")), 2)
    
  51. 
    
  52.         self.assertEqual(len(Event.objects.values()), 2)
    
  53.         self.assertEqual(len(Event.objects.values("screening__pk")), 2)
    
  54.         self.assertEqual(len(Event.objects.values("screening__movie__pk")), 2)
    
  55.         self.assertEqual(len(Event.objects.values("screening__movie__title")), 2)
    
  56.         # This failed.
    
  57.         self.assertEqual(
    
  58.             len(
    
  59.                 Event.objects.values("screening__movie__pk", "screening__movie__title")
    
  60.             ),
    
  61.             2,
    
  62.         )
    
  63. 
    
  64.         # Simple filter/exclude queries for good measure.
    
  65.         self.assertEqual(Event.objects.filter(screening__movie=self.movie).count(), 1)
    
  66.         self.assertEqual(Event.objects.exclude(screening__movie=self.movie).count(), 1)
    
  67. 
    
  68.     # These all work because the second foreign key in the chain has null=True.
    
  69.     def test_inheritance_null_FK(self):
    
  70.         Event.objects.create()
    
  71.         ScreeningNullFK.objects.create(movie=None)
    
  72.         ScreeningNullFK.objects.create(movie=self.movie)
    
  73. 
    
  74.         self.assertEqual(len(Event.objects.all()), 3)
    
  75.         self.assertEqual(len(Event.objects.select_related("screeningnullfk")), 3)
    
  76.         self.assertEqual(len(Event.objects.select_related("screeningnullfk__movie")), 3)
    
  77. 
    
  78.         self.assertEqual(len(Event.objects.values()), 3)
    
  79.         self.assertEqual(len(Event.objects.values("screeningnullfk__pk")), 3)
    
  80.         self.assertEqual(len(Event.objects.values("screeningnullfk__movie__pk")), 3)
    
  81.         self.assertEqual(len(Event.objects.values("screeningnullfk__movie__title")), 3)
    
  82.         self.assertEqual(
    
  83.             len(
    
  84.                 Event.objects.values(
    
  85.                     "screeningnullfk__movie__pk", "screeningnullfk__movie__title"
    
  86.                 )
    
  87.             ),
    
  88.             3,
    
  89.         )
    
  90. 
    
  91.         self.assertEqual(
    
  92.             Event.objects.filter(screeningnullfk__movie=self.movie).count(), 1
    
  93.         )
    
  94.         self.assertEqual(
    
  95.             Event.objects.exclude(screeningnullfk__movie=self.movie).count(), 2
    
  96.         )
    
  97. 
    
  98.     def test_null_exclude(self):
    
  99.         screening = ScreeningNullFK.objects.create(movie=None)
    
  100.         ScreeningNullFK.objects.create(movie=self.movie)
    
  101.         self.assertEqual(
    
  102.             list(ScreeningNullFK.objects.exclude(movie__id=self.movie.pk)), [screening]
    
  103.         )
    
  104. 
    
  105.     # This test failed in #16715 because in some cases INNER JOIN was selected
    
  106.     # for the second foreign key relation instead of LEFT OUTER JOIN.
    
  107.     def test_explicit_ForeignKey(self):
    
  108.         Package.objects.create()
    
  109.         screening = Screening.objects.create(movie=self.movie)
    
  110.         Package.objects.create(screening=screening)
    
  111. 
    
  112.         self.assertEqual(len(Package.objects.all()), 2)
    
  113.         self.assertEqual(len(Package.objects.select_related("screening")), 2)
    
  114.         self.assertEqual(len(Package.objects.select_related("screening__movie")), 2)
    
  115. 
    
  116.         self.assertEqual(len(Package.objects.values()), 2)
    
  117.         self.assertEqual(len(Package.objects.values("screening__pk")), 2)
    
  118.         self.assertEqual(len(Package.objects.values("screening__movie__pk")), 2)
    
  119.         self.assertEqual(len(Package.objects.values("screening__movie__title")), 2)
    
  120.         # This failed.
    
  121.         self.assertEqual(
    
  122.             len(
    
  123.                 Package.objects.values(
    
  124.                     "screening__movie__pk", "screening__movie__title"
    
  125.                 )
    
  126.             ),
    
  127.             2,
    
  128.         )
    
  129. 
    
  130.         self.assertEqual(Package.objects.filter(screening__movie=self.movie).count(), 1)
    
  131.         self.assertEqual(
    
  132.             Package.objects.exclude(screening__movie=self.movie).count(), 1
    
  133.         )
    
  134. 
    
  135.     # These all work because the second foreign key in the chain has null=True.
    
  136.     def test_explicit_ForeignKey_NullFK(self):
    
  137.         PackageNullFK.objects.create()
    
  138.         screening = ScreeningNullFK.objects.create(movie=None)
    
  139.         screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
    
  140.         PackageNullFK.objects.create(screening=screening)
    
  141.         PackageNullFK.objects.create(screening=screening_with_movie)
    
  142. 
    
  143.         self.assertEqual(len(PackageNullFK.objects.all()), 3)
    
  144.         self.assertEqual(len(PackageNullFK.objects.select_related("screening")), 3)
    
  145.         self.assertEqual(
    
  146.             len(PackageNullFK.objects.select_related("screening__movie")), 3
    
  147.         )
    
  148. 
    
  149.         self.assertEqual(len(PackageNullFK.objects.values()), 3)
    
  150.         self.assertEqual(len(PackageNullFK.objects.values("screening__pk")), 3)
    
  151.         self.assertEqual(len(PackageNullFK.objects.values("screening__movie__pk")), 3)
    
  152.         self.assertEqual(
    
  153.             len(PackageNullFK.objects.values("screening__movie__title")), 3
    
  154.         )
    
  155.         self.assertEqual(
    
  156.             len(
    
  157.                 PackageNullFK.objects.values(
    
  158.                     "screening__movie__pk", "screening__movie__title"
    
  159.                 )
    
  160.             ),
    
  161.             3,
    
  162.         )
    
  163. 
    
  164.         self.assertEqual(
    
  165.             PackageNullFK.objects.filter(screening__movie=self.movie).count(), 1
    
  166.         )
    
  167.         self.assertEqual(
    
  168.             PackageNullFK.objects.exclude(screening__movie=self.movie).count(), 2
    
  169.         )
    
  170. 
    
  171. 
    
  172. # Some additional tests for #16715. The only difference is the depth of the
    
  173. # nesting as we now use 4 models instead of 3 (and thus 3 relations). This
    
  174. # checks if promotion of join types works for deeper nesting too.
    
  175. class DeeplyNestedForeignKeysTests(TestCase):
    
  176.     @classmethod
    
  177.     def setUpTestData(cls):
    
  178.         cls.director = Person.objects.create(name="Terry Gilliam / Terry Jones")
    
  179.         cls.movie = Movie.objects.create(
    
  180.             title="Monty Python and the Holy Grail", director=cls.director
    
  181.         )
    
  182. 
    
  183.     def test_inheritance(self):
    
  184.         Event.objects.create()
    
  185.         Screening.objects.create(movie=self.movie)
    
  186. 
    
  187.         self.assertEqual(len(Event.objects.all()), 2)
    
  188.         self.assertEqual(
    
  189.             len(Event.objects.select_related("screening__movie__director")), 2
    
  190.         )
    
  191. 
    
  192.         self.assertEqual(len(Event.objects.values()), 2)
    
  193.         self.assertEqual(len(Event.objects.values("screening__movie__director__pk")), 2)
    
  194.         self.assertEqual(
    
  195.             len(Event.objects.values("screening__movie__director__name")), 2
    
  196.         )
    
  197.         self.assertEqual(
    
  198.             len(
    
  199.                 Event.objects.values(
    
  200.                     "screening__movie__director__pk", "screening__movie__director__name"
    
  201.                 )
    
  202.             ),
    
  203.             2,
    
  204.         )
    
  205.         self.assertEqual(
    
  206.             len(
    
  207.                 Event.objects.values(
    
  208.                     "screening__movie__pk", "screening__movie__director__pk"
    
  209.                 )
    
  210.             ),
    
  211.             2,
    
  212.         )
    
  213.         self.assertEqual(
    
  214.             len(
    
  215.                 Event.objects.values(
    
  216.                     "screening__movie__pk", "screening__movie__director__name"
    
  217.                 )
    
  218.             ),
    
  219.             2,
    
  220.         )
    
  221.         self.assertEqual(
    
  222.             len(
    
  223.                 Event.objects.values(
    
  224.                     "screening__movie__title", "screening__movie__director__pk"
    
  225.                 )
    
  226.             ),
    
  227.             2,
    
  228.         )
    
  229.         self.assertEqual(
    
  230.             len(
    
  231.                 Event.objects.values(
    
  232.                     "screening__movie__title", "screening__movie__director__name"
    
  233.                 )
    
  234.             ),
    
  235.             2,
    
  236.         )
    
  237. 
    
  238.         self.assertEqual(
    
  239.             Event.objects.filter(screening__movie__director=self.director).count(), 1
    
  240.         )
    
  241.         self.assertEqual(
    
  242.             Event.objects.exclude(screening__movie__director=self.director).count(), 1
    
  243.         )
    
  244. 
    
  245.     def test_explicit_ForeignKey(self):
    
  246.         Package.objects.create()
    
  247.         screening = Screening.objects.create(movie=self.movie)
    
  248.         Package.objects.create(screening=screening)
    
  249. 
    
  250.         self.assertEqual(len(Package.objects.all()), 2)
    
  251.         self.assertEqual(
    
  252.             len(Package.objects.select_related("screening__movie__director")), 2
    
  253.         )
    
  254. 
    
  255.         self.assertEqual(len(Package.objects.values()), 2)
    
  256.         self.assertEqual(
    
  257.             len(Package.objects.values("screening__movie__director__pk")), 2
    
  258.         )
    
  259.         self.assertEqual(
    
  260.             len(Package.objects.values("screening__movie__director__name")), 2
    
  261.         )
    
  262.         self.assertEqual(
    
  263.             len(
    
  264.                 Package.objects.values(
    
  265.                     "screening__movie__director__pk", "screening__movie__director__name"
    
  266.                 )
    
  267.             ),
    
  268.             2,
    
  269.         )
    
  270.         self.assertEqual(
    
  271.             len(
    
  272.                 Package.objects.values(
    
  273.                     "screening__movie__pk", "screening__movie__director__pk"
    
  274.                 )
    
  275.             ),
    
  276.             2,
    
  277.         )
    
  278.         self.assertEqual(
    
  279.             len(
    
  280.                 Package.objects.values(
    
  281.                     "screening__movie__pk", "screening__movie__director__name"
    
  282.                 )
    
  283.             ),
    
  284.             2,
    
  285.         )
    
  286.         self.assertEqual(
    
  287.             len(
    
  288.                 Package.objects.values(
    
  289.                     "screening__movie__title", "screening__movie__director__pk"
    
  290.                 )
    
  291.             ),
    
  292.             2,
    
  293.         )
    
  294.         self.assertEqual(
    
  295.             len(
    
  296.                 Package.objects.values(
    
  297.                     "screening__movie__title", "screening__movie__director__name"
    
  298.                 )
    
  299.             ),
    
  300.             2,
    
  301.         )
    
  302. 
    
  303.         self.assertEqual(
    
  304.             Package.objects.filter(screening__movie__director=self.director).count(), 1
    
  305.         )
    
  306.         self.assertEqual(
    
  307.             Package.objects.exclude(screening__movie__director=self.director).count(), 1
    
  308.         )