these are tests for 16715 the basic scheme is always the same 3 models with 2 relations the first relation may be null while the second is nonnullable in some cases django would pick the wrong join type for the second relation resulting in missing objects in the queryset model a relation ab nullable model b relation bc nonnullable model c because of the possibility of null rows resulting from the left outer join between model a and model b i e instances of a without reference to b the second join must also be left outer join so that we do not ignore instances of a that do not reference b relation ab can either be an explicit foreign key or an implicit reverse relation such as introduced by onetoone relations through multitable inheritance this test failed in 16715 because in some cases inner join was selected for the second foreign key relation instead of left outer join this failed this failed simple filterexclude queries for good measure these all work because the second foreign key in the chain has nulltrue this test failed in 16715 because in some cases inner join was selected for the second foreign key relation instead of left outer join this failed these all work because the second foreign key in the chain has nulltrue some additional tests for 16715 the only difference is the depth of the nesting as we now use 4 models instead of 3 and thus 3 relations this checks if promotion of join types works for deeper nesting too these are tests for 16715 the basic scheme is always the same 3 models with 2 relations the first relation may be null while the second is non nullable in some cases django would pick the wrong join type for the second relation resulting in missing objects in the queryset model a relation a b nullable model b relation b c non nullable model c because of the possibility of null rows resulting from the left outer join between model a and model b i e instances of a without reference to b the second join must also be left outer join so that we do not ignore instances of a that do not reference b relation a b can either be an explicit foreign key or an implicit reverse relation such as introduced by one to one relations through multi table inheritance this test failed in 16715 because in some cases inner join was selected for the second foreign key relation instead of left outer join this failed this failed simple filter exclude queries for good measure these all work because the second foreign key in the chain has null true this test failed in 16715 because in some cases inner join was selected for the second foreign key relation instead of left outer join this failed these all work because the second foreign key in the chain has null true some additional tests for 16715 the only difference is the depth of the nesting as we now use 4 models instead of 3 and thus 3 relations this checks if promotion of join types works for deeper nesting too
from django.test import TestCase

from .models import (
    Event,
    Movie,
    Package,
    PackageNullFK,
    Person,
    Screening,
    ScreeningNullFK,
)





















class NestedForeignKeysTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.director = Person.objects.create(name="Terry Gilliam / Terry Jones")
        cls.movie = Movie.objects.create(
            title="Monty Python and the Holy Grail", director=cls.director
        )

    
    
    def test_inheritance(self):
        Event.objects.create()
        Screening.objects.create(movie=self.movie)

        self.assertEqual(len(Event.objects.all()), 2)
        self.assertEqual(len(Event.objects.select_related("screening")), 2)
        
        self.assertEqual(len(Event.objects.select_related("screening__movie")), 2)

        self.assertEqual(len(Event.objects.values()), 2)
        self.assertEqual(len(Event.objects.values("screening__pk")), 2)
        self.assertEqual(len(Event.objects.values("screening__movie__pk")), 2)
        self.assertEqual(len(Event.objects.values("screening__movie__title")), 2)
        
        self.assertEqual(
            len(
                Event.objects.values("screening__movie__pk", "screening__movie__title")
            ),
            2,
        )

        
        self.assertEqual(Event.objects.filter(screening__movie=self.movie).count(), 1)
        self.assertEqual(Event.objects.exclude(screening__movie=self.movie).count(), 1)

    
    def test_inheritance_null_FK(self):
        Event.objects.create()
        ScreeningNullFK.objects.create(movie=None)
        ScreeningNullFK.objects.create(movie=self.movie)

        self.assertEqual(len(Event.objects.all()), 3)
        self.assertEqual(len(Event.objects.select_related("screeningnullfk")), 3)
        self.assertEqual(len(Event.objects.select_related("screeningnullfk__movie")), 3)

        self.assertEqual(len(Event.objects.values()), 3)
        self.assertEqual(len(Event.objects.values("screeningnullfk__pk")), 3)
        self.assertEqual(len(Event.objects.values("screeningnullfk__movie__pk")), 3)
        self.assertEqual(len(Event.objects.values("screeningnullfk__movie__title")), 3)
        self.assertEqual(
            len(
                Event.objects.values(
                    "screeningnullfk__movie__pk", "screeningnullfk__movie__title"
                )
            ),
            3,
        )

        self.assertEqual(
            Event.objects.filter(screeningnullfk__movie=self.movie).count(), 1
        )
        self.assertEqual(
            Event.objects.exclude(screeningnullfk__movie=self.movie).count(), 2
        )

    def test_null_exclude(self):
        screening = ScreeningNullFK.objects.create(movie=None)
        ScreeningNullFK.objects.create(movie=self.movie)
        self.assertEqual(
            list(ScreeningNullFK.objects.exclude(movie__id=self.movie.pk)), [screening]
        )

    
    
    def test_explicit_ForeignKey(self):
        Package.objects.create()
        screening = Screening.objects.create(movie=self.movie)
        Package.objects.create(screening=screening)

        self.assertEqual(len(Package.objects.all()), 2)
        self.assertEqual(len(Package.objects.select_related("screening")), 2)
        self.assertEqual(len(Package.objects.select_related("screening__movie")), 2)

        self.assertEqual(len(Package.objects.values()), 2)
        self.assertEqual(len(Package.objects.values("screening__pk")), 2)
        self.assertEqual(len(Package.objects.values("screening__movie__pk")), 2)
        self.assertEqual(len(Package.objects.values("screening__movie__title")), 2)
        
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__pk", "screening__movie__title"
                )
            ),
            2,
        )

        self.assertEqual(Package.objects.filter(screening__movie=self.movie).count(), 1)
        self.assertEqual(
            Package.objects.exclude(screening__movie=self.movie).count(), 1
        )

    
    def test_explicit_ForeignKey_NullFK(self):
        PackageNullFK.objects.create()
        screening = ScreeningNullFK.objects.create(movie=None)
        screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
        PackageNullFK.objects.create(screening=screening)
        PackageNullFK.objects.create(screening=screening_with_movie)

        self.assertEqual(len(PackageNullFK.objects.all()), 3)
        self.assertEqual(len(PackageNullFK.objects.select_related("screening")), 3)
        self.assertEqual(
            len(PackageNullFK.objects.select_related("screening__movie")), 3
        )

        self.assertEqual(len(PackageNullFK.objects.values()), 3)
        self.assertEqual(len(PackageNullFK.objects.values("screening__pk")), 3)
        self.assertEqual(len(PackageNullFK.objects.values("screening__movie__pk")), 3)
        self.assertEqual(
            len(PackageNullFK.objects.values("screening__movie__title")), 3
        )
        self.assertEqual(
            len(
                PackageNullFK.objects.values(
                    "screening__movie__pk", "screening__movie__title"
                )
            ),
            3,
        )

        self.assertEqual(
            PackageNullFK.objects.filter(screening__movie=self.movie).count(), 1
        )
        self.assertEqual(
            PackageNullFK.objects.exclude(screening__movie=self.movie).count(), 2
        )





class DeeplyNestedForeignKeysTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.director = Person.objects.create(name="Terry Gilliam / Terry Jones")
        cls.movie = Movie.objects.create(
            title="Monty Python and the Holy Grail", director=cls.director
        )

    def test_inheritance(self):
        Event.objects.create()
        Screening.objects.create(movie=self.movie)

        self.assertEqual(len(Event.objects.all()), 2)
        self.assertEqual(
            len(Event.objects.select_related("screening__movie__director")), 2
        )

        self.assertEqual(len(Event.objects.values()), 2)
        self.assertEqual(len(Event.objects.values("screening__movie__director__pk")), 2)
        self.assertEqual(
            len(Event.objects.values("screening__movie__director__name")), 2
        )
        self.assertEqual(
            len(
                Event.objects.values(
                    "screening__movie__director__pk", "screening__movie__director__name"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Event.objects.values(
                    "screening__movie__pk", "screening__movie__director__pk"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Event.objects.values(
                    "screening__movie__pk", "screening__movie__director__name"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Event.objects.values(
                    "screening__movie__title", "screening__movie__director__pk"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Event.objects.values(
                    "screening__movie__title", "screening__movie__director__name"
                )
            ),
            2,
        )

        self.assertEqual(
            Event.objects.filter(screening__movie__director=self.director).count(), 1
        )
        self.assertEqual(
            Event.objects.exclude(screening__movie__director=self.director).count(), 1
        )

    def test_explicit_ForeignKey(self):
        Package.objects.create()
        screening = Screening.objects.create(movie=self.movie)
        Package.objects.create(screening=screening)

        self.assertEqual(len(Package.objects.all()), 2)
        self.assertEqual(
            len(Package.objects.select_related("screening__movie__director")), 2
        )

        self.assertEqual(len(Package.objects.values()), 2)
        self.assertEqual(
            len(Package.objects.values("screening__movie__director__pk")), 2
        )
        self.assertEqual(
            len(Package.objects.values("screening__movie__director__name")), 2
        )
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__director__pk", "screening__movie__director__name"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__pk", "screening__movie__director__pk"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__pk", "screening__movie__director__name"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__title", "screening__movie__director__pk"
                )
            ),
            2,
        )
        self.assertEqual(
            len(
                Package.objects.values(
                    "screening__movie__title", "screening__movie__director__name"
                )
            ),
            2,
        )

        self.assertEqual(
            Package.objects.filter(screening__movie__director=self.director).count(), 1
        )
        self.assertEqual(
            Package.objects.exclude(screening__movie__director=self.director).count(), 1
        )