mirror of
				https://github.com/django/django.git
				synced 2025-11-04 05:35:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			176 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from __future__ import unicode_literals
 | 
						|
 | 
						|
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):
 | 
						|
    def setUp(self):
 | 
						|
        self.director = Person.objects.create(name='Terry Gilliam / Terry Jones')
 | 
						|
        self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.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):
 | 
						|
    def setUp(self):
 | 
						|
        self.director = Person.objects.create(name='Terry Gilliam / Terry Jones')
 | 
						|
        self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.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)
 |