mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			309 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from django.test import TestCase
 | |
| 
 | |
| from .models import (
 | |
|     Event,
 | |
|     Movie,
 | |
|     Package,
 | |
|     PackageNullFK,
 | |
|     Person,
 | |
|     Screening,
 | |
|     ScreeningNullFK,
 | |
| )
 | |
| 
 | |
| 
 | |
| # 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).
 | |
| 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
 | |
|         )
 | |
| 
 | |
|     # This test failed in #16715 because in some cases INNER JOIN was selected
 | |
|     # for the second foreign key relation instead of LEFT OUTER JOIN.
 | |
|     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)
 | |
|         # This failed.
 | |
|         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)
 | |
|         # This failed.
 | |
|         self.assertEqual(
 | |
|             len(
 | |
|                 Event.objects.values("screening__movie__pk", "screening__movie__title")
 | |
|             ),
 | |
|             2,
 | |
|         )
 | |
| 
 | |
|         # Simple filter/exclude queries for good measure.
 | |
|         self.assertEqual(Event.objects.filter(screening__movie=self.movie).count(), 1)
 | |
|         self.assertEqual(Event.objects.exclude(screening__movie=self.movie).count(), 1)
 | |
| 
 | |
|     # These all work because the second foreign key in the chain has null=True.
 | |
|     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]
 | |
|         )
 | |
| 
 | |
|     # This test failed in #16715 because in some cases INNER JOIN was selected
 | |
|     # for the second foreign key relation instead of LEFT OUTER JOIN.
 | |
|     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)
 | |
|         # This failed.
 | |
|         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
 | |
|         )
 | |
| 
 | |
|     # These all work because the second foreign key in the chain has null=True.
 | |
|     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
 | |
|         )
 | |
| 
 | |
| 
 | |
| # 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.
 | |
| 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
 | |
|         )
 |