1
from __future__ import absolute_import
2
from django.test import TestCase
4
from .models import Person, Movie, Event, Screening, ScreeningNullFK, Package, PackageNullFK
7
# These are tests for #16715. The basic scheme is always the same: 3 models with
8
# 2 relations. The first relation may be null, while the second is non-nullable.
9
# In some cases, Django would pick the wrong join type for the second relation,
10
# resulting in missing objects in the queryset.
13
# | (Relation A/B : nullable)
15
# | (Relation B/C : non-nullable)
18
# Because of the possibility of NULL rows resulting from the LEFT OUTER JOIN
19
# between Model A and Model B (i.e. instances of A without reference to B),
20
# the second join must also be LEFT OUTER JOIN, so that we do not ignore
21
# instances of A that do not reference B.
23
# Relation A/B can either be an explicit foreign key or an implicit reverse
24
# relation such as introduced by one-to-one relations (through multi-table
26
class NestedForeignKeysTests(TestCase):
28
self.director = Person.objects.create(name='Terry Gilliam / Terry Jones')
29
self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.director)
32
# This test failed in #16715 because in some cases INNER JOIN was selected
33
# for the second foreign key relation instead of LEFT OUTER JOIN.
34
def testInheritance(self):
35
some_event = Event.objects.create()
36
screening = Screening.objects.create(movie=self.movie)
38
self.assertEqual(len(Event.objects.all()), 2)
39
self.assertEqual(len(Event.objects.select_related('screening')), 2)
41
self.assertEqual(len(Event.objects.select_related('screening__movie')), 2)
43
self.assertEqual(len(Event.objects.values()), 2)
44
self.assertEqual(len(Event.objects.values('screening__pk')), 2)
45
self.assertEqual(len(Event.objects.values('screening__movie__pk')), 2)
46
self.assertEqual(len(Event.objects.values('screening__movie__title')), 2)
48
self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__title')), 2)
50
# Simple filter/exclude queries for good measure.
51
self.assertEqual(Event.objects.filter(screening__movie=self.movie).count(), 1)
52
self.assertEqual(Event.objects.exclude(screening__movie=self.movie).count(), 1)
55
# These all work because the second foreign key in the chain has null=True.
56
def testInheritanceNullFK(self):
57
some_event = Event.objects.create()
58
screening = ScreeningNullFK.objects.create(movie=None)
59
screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
61
self.assertEqual(len(Event.objects.all()), 3)
62
self.assertEqual(len(Event.objects.select_related('screeningnullfk')), 3)
63
self.assertEqual(len(Event.objects.select_related('screeningnullfk__movie')), 3)
65
self.assertEqual(len(Event.objects.values()), 3)
66
self.assertEqual(len(Event.objects.values('screeningnullfk__pk')), 3)
67
self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk')), 3)
68
self.assertEqual(len(Event.objects.values('screeningnullfk__movie__title')), 3)
69
self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk', 'screeningnullfk__movie__title')), 3)
71
self.assertEqual(Event.objects.filter(screeningnullfk__movie=self.movie).count(), 1)
72
self.assertEqual(Event.objects.exclude(screeningnullfk__movie=self.movie).count(), 2)
74
def test_null_exclude(self):
75
screening = ScreeningNullFK.objects.create(movie=None)
76
ScreeningNullFK.objects.create(movie=self.movie)
78
list(ScreeningNullFK.objects.exclude(movie__id=self.movie.pk)),
81
# This test failed in #16715 because in some cases INNER JOIN was selected
82
# for the second foreign key relation instead of LEFT OUTER JOIN.
83
def testExplicitForeignKey(self):
84
package = Package.objects.create()
85
screening = Screening.objects.create(movie=self.movie)
86
package_with_screening = Package.objects.create(screening=screening)
88
self.assertEqual(len(Package.objects.all()), 2)
89
self.assertEqual(len(Package.objects.select_related('screening')), 2)
90
self.assertEqual(len(Package.objects.select_related('screening__movie')), 2)
92
self.assertEqual(len(Package.objects.values()), 2)
93
self.assertEqual(len(Package.objects.values('screening__pk')), 2)
94
self.assertEqual(len(Package.objects.values('screening__movie__pk')), 2)
95
self.assertEqual(len(Package.objects.values('screening__movie__title')), 2)
97
self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__title')), 2)
99
self.assertEqual(Package.objects.filter(screening__movie=self.movie).count(), 1)
100
self.assertEqual(Package.objects.exclude(screening__movie=self.movie).count(), 1)
103
# These all work because the second foreign key in the chain has null=True.
104
def testExplicitForeignKeyNullFK(self):
105
package = PackageNullFK.objects.create()
106
screening = ScreeningNullFK.objects.create(movie=None)
107
screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
108
package_with_screening = PackageNullFK.objects.create(screening=screening)
109
package_with_screening_with_movie = PackageNullFK.objects.create(screening=screening_with_movie)
111
self.assertEqual(len(PackageNullFK.objects.all()), 3)
112
self.assertEqual(len(PackageNullFK.objects.select_related('screening')), 3)
113
self.assertEqual(len(PackageNullFK.objects.select_related('screening__movie')), 3)
115
self.assertEqual(len(PackageNullFK.objects.values()), 3)
116
self.assertEqual(len(PackageNullFK.objects.values('screening__pk')), 3)
117
self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk')), 3)
118
self.assertEqual(len(PackageNullFK.objects.values('screening__movie__title')), 3)
119
self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk', 'screening__movie__title')), 3)
121
self.assertEqual(PackageNullFK.objects.filter(screening__movie=self.movie).count(), 1)
122
self.assertEqual(PackageNullFK.objects.exclude(screening__movie=self.movie).count(), 2)
125
# Some additional tests for #16715. The only difference is the depth of the
126
# nesting as we now use 4 models instead of 3 (and thus 3 relations). This
127
# checks if promotion of join types works for deeper nesting too.
128
class DeeplyNestedForeignKeysTests(TestCase):
130
self.director = Person.objects.create(name='Terry Gilliam / Terry Jones')
131
self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.director)
134
def testInheritance(self):
135
some_event = Event.objects.create()
136
screening = Screening.objects.create(movie=self.movie)
138
self.assertEqual(len(Event.objects.all()), 2)
139
self.assertEqual(len(Event.objects.select_related('screening__movie__director')), 2)
141
self.assertEqual(len(Event.objects.values()), 2)
142
self.assertEqual(len(Event.objects.values('screening__movie__director__pk')), 2)
143
self.assertEqual(len(Event.objects.values('screening__movie__director__name')), 2)
144
self.assertEqual(len(Event.objects.values('screening__movie__director__pk', 'screening__movie__director__name')), 2)
145
self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__director__pk')), 2)
146
self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__director__name')), 2)
147
self.assertEqual(len(Event.objects.values('screening__movie__title', 'screening__movie__director__pk')), 2)
148
self.assertEqual(len(Event.objects.values('screening__movie__title', 'screening__movie__director__name')), 2)
150
self.assertEqual(Event.objects.filter(screening__movie__director=self.director).count(), 1)
151
self.assertEqual(Event.objects.exclude(screening__movie__director=self.director).count(), 1)
154
def testExplicitForeignKey(self):
155
package = Package.objects.create()
156
screening = Screening.objects.create(movie=self.movie)
157
package_with_screening = Package.objects.create(screening=screening)
159
self.assertEqual(len(Package.objects.all()), 2)
160
self.assertEqual(len(Package.objects.select_related('screening__movie__director')), 2)
162
self.assertEqual(len(Package.objects.values()), 2)
163
self.assertEqual(len(Package.objects.values('screening__movie__director__pk')), 2)
164
self.assertEqual(len(Package.objects.values('screening__movie__director__name')), 2)
165
self.assertEqual(len(Package.objects.values('screening__movie__director__pk', 'screening__movie__director__name')), 2)
166
self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__director__pk')), 2)
167
self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__director__name')), 2)
168
self.assertEqual(len(Package.objects.values('screening__movie__title', 'screening__movie__director__pk')), 2)
169
self.assertEqual(len(Package.objects.values('screening__movie__title', 'screening__movie__director__name')), 2)
171
self.assertEqual(Package.objects.filter(screening__movie__director=self.director).count(), 1)
172
self.assertEqual(Package.objects.exclude(screening__movie__director=self.director).count(), 1)