Ticket #16715: nested-foreign-key-test-r16914.patch

File nested-foreign-key-test-r16914.patch, 6.3 KB (added by Sebastian Goll, 13 years ago)
  • tests/regressiontests/nested_foreign_keys/tests.py

     
     1from django.test import TestCase
     2
     3from models import Movie, Event, Screening, ScreeningNullFK, Package, PackageNullFK
     4
     5
     6# These are tests for #16715. The basic scheme is always the same: 3 models with
     7# 2 relations. The first relation may be null, while the second is non-nullable.
     8# In some cases, Django would pick the wrong join type for the second relation,
     9# resulting in missing objects in the queryset.
     10#
     11#   Model A
     12#   | (Relation A/B : nullable)
     13#   Model B
     14#   | (Relation B/C : non-nullable)
     15#   Model C
     16#
     17# Because of the possibility of NULL rows resulting from the LEFT OUTER JOIN
     18# between Model A and Model B (i.e. instances of A without reference to B),
     19# the second join must also be LEFT OUTER JOIN, so that we do not ignore
     20# instances of A that do not reference B.
     21#
     22# Relation A/B can either be an explicit foreign key or an implicit reverse
     23# relation such as introduced by one-to-one relations (through multi-table
     24# inheritance).
     25class NestedForeignKeysTests(TestCase):
     26    def setUp(self):
     27        self.movie = Movie.objects.create(title='Monty Python and the Holy Grail')
     28
     29
     30    # This test failed in #16715 because in some cases INNER JOIN was selected
     31    # for the second foreign key relation instead of LEFT OUTER JOIN.
     32    def testInheritance(self):
     33        some_event = Event.objects.create()
     34        screening = Screening.objects.create(movie=self.movie)
     35
     36        self.assertEqual(len(Event.objects.all()), 2)
     37        self.assertEqual(len(Event.objects.select_related('screening')), 2)
     38        # This failed.
     39        self.assertEqual(len(Event.objects.select_related('screening__movie')), 2)
     40
     41        self.assertEqual(len(Event.objects.values()), 2)
     42        self.assertEqual(len(Event.objects.values('screening__pk')), 2)
     43        self.assertEqual(len(Event.objects.values('screening__movie__pk')), 2)
     44        self.assertEqual(len(Event.objects.values('screening__movie__title')), 2)
     45        # This failed.
     46        self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__title')), 2)
     47
     48
     49    # These all work because the second foreign key in the chain has null=True.
     50    def testInheritanceNullFK(self):
     51        some_event = Event.objects.create()
     52        screening = ScreeningNullFK.objects.create(movie=None)
     53        screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
     54
     55        self.assertEqual(len(Event.objects.all()), 3)
     56        self.assertEqual(len(Event.objects.select_related('screeningnullfk')), 3)
     57        self.assertEqual(len(Event.objects.select_related('screeningnullfk__movie')), 3)
     58
     59        self.assertEqual(len(Event.objects.values()), 3)
     60        self.assertEqual(len(Event.objects.values('screeningnullfk__pk')), 3)
     61        self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk')), 3)
     62        self.assertEqual(len(Event.objects.values('screeningnullfk__movie__title')), 3)
     63        self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk', 'screeningnullfk__movie__title')), 3)
     64
     65
     66    # This test failed in #16715 because in some cases INNER JOIN was selected
     67    # for the second foreign key relation instead of LEFT OUTER JOIN.
     68    def testExplicitForeignKey(self):
     69        package = Package.objects.create()
     70        screening = Screening.objects.create(movie=self.movie)
     71        package_with_screening = Package.objects.create(screening=screening)
     72
     73        self.assertEqual(len(Package.objects.all()), 2)
     74        self.assertEqual(len(Package.objects.select_related('screening')), 2)
     75        self.assertEqual(len(Package.objects.select_related('screening__movie')), 2)
     76
     77        self.assertEqual(len(Package.objects.values()), 2)
     78        self.assertEqual(len(Package.objects.values('screening__pk')), 2)
     79        self.assertEqual(len(Package.objects.values('screening__movie__pk')), 2)
     80        self.assertEqual(len(Package.objects.values('screening__movie__title')), 2)
     81        # This failed.
     82        self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__title')), 2)
     83
     84
     85    # These all work because the second foreign key in the chain has null=True.
     86    def testExplicitForeignKeyNullFK(self):
     87        package = PackageNullFK.objects.create()
     88        screening = ScreeningNullFK.objects.create(movie=None)
     89        screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie)
     90        package_with_screening = PackageNullFK.objects.create(screening=screening)
     91        package_with_screening_with_movie = PackageNullFK.objects.create(screening=screening_with_movie)
     92
     93        self.assertEqual(len(PackageNullFK.objects.all()), 3)
     94        self.assertEqual(len(PackageNullFK.objects.select_related('screening')), 3)
     95        self.assertEqual(len(PackageNullFK.objects.select_related('screening__movie')), 3)
     96
     97        self.assertEqual(len(PackageNullFK.objects.values()), 3)
     98        self.assertEqual(len(PackageNullFK.objects.values('screening__pk')), 3)
     99        self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk')), 3)
     100        self.assertEqual(len(PackageNullFK.objects.values('screening__movie__title')), 3)
     101        self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk', 'screening__movie__title')), 3)
  • tests/regressiontests/nested_foreign_keys/models.py

     
     1from django.db import models
     2
     3
     4class Movie(models.Model):
     5    title = models.CharField(max_length=200)
     6
     7
     8class Event(models.Model):
     9    pass
     10
     11
     12class Screening(Event):
     13    movie = models.ForeignKey(Movie)
     14
     15class ScreeningNullFK(Event):
     16    movie = models.ForeignKey(Movie, null=True)
     17
     18
     19class Package(models.Model):
     20    screening = models.ForeignKey(Screening, null=True)
     21
     22class PackageNullFK(models.Model):
     23    screening = models.ForeignKey(ScreeningNullFK, null=True)
Back to Top