diff options
author | Anssi Kääriäinen <akaariai@gmail.com> | 2013-06-06 00:29:44 +0300 |
---|---|---|
committer | Anssi Kääriäinen <akaariai@gmail.com> | 2013-06-06 01:54:46 +0300 |
commit | 31fd64ad8a98d7de0acb9144ae6f7bd124700cb0 (patch) | |
tree | b3c8fa23035de4e88ceab1e21afee6f60c40052a /tests/generic_relations_regress | |
parent | 8c5b805ca11ff25ffa8a4efd0781efe5cc872de3 (diff) | |
download | django-31fd64ad8a98d7de0acb9144ae6f7bd124700cb0.tar.gz |
Fixed #20564 -- Generic relations exclude() regression
The patch for #19385 caused a regression in certain generic relations
.exclude() filters if a subquery was needed. The fix contains a
refactoring to how Query.split_exclude() and Query.trim_start()
interact.
Thanks to Trac alias nferrari for the report.
Diffstat (limited to 'tests/generic_relations_regress')
-rw-r--r-- | tests/generic_relations_regress/models.py | 24 | ||||
-rw-r--r-- | tests/generic_relations_regress/tests.py | 58 |
2 files changed, 81 insertions, 1 deletions
diff --git a/tests/generic_relations_regress/models.py b/tests/generic_relations_regress/models.py index 206dbe02b0..d716f09058 100644 --- a/tests/generic_relations_regress/models.py +++ b/tests/generic_relations_regress/models.py @@ -131,3 +131,27 @@ class HasLinks(models.Model): class HasLinkThing(HasLinks): pass + +class A(models.Model): + flag = models.NullBooleanField() + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey('content_type', 'object_id') + +class B(models.Model): + a = generic.GenericRelation(A) + + class Meta: + ordering = ('id',) + +class C(models.Model): + b = models.ForeignKey(B) + + class Meta: + ordering = ('id',) + +class D(models.Model): + b = models.ForeignKey(B, null=True) + + class Meta: + ordering = ('id',) diff --git a/tests/generic_relations_regress/tests.py b/tests/generic_relations_regress/tests.py index 1edff6847e..75edff34f2 100644 --- a/tests/generic_relations_regress/tests.py +++ b/tests/generic_relations_regress/tests.py @@ -5,7 +5,7 @@ from django.test import TestCase, skipIfDBFeature from .models import ( Address, Place, Restaurant, Link, CharLink, TextLink, Person, Contact, Note, Organization, OddRelation1, OddRelation2, Company, - Developer, Team, Guild, Tag, Board, HasLinkThing) + Developer, Team, Guild, Tag, Board, HasLinkThing, A, B, C, D) class GenericRelationTests(TestCase): @@ -156,3 +156,59 @@ class GenericRelationTests(TestCase): self.assertQuerysetEqual( HasLinkThing.objects.exclude(links=l1), [hs2], lambda x: x) + + def test_ticket_20564(self): + b1 = B.objects.create() + b2 = B.objects.create() + b3 = B.objects.create() + c1 = C.objects.create(b=b1) + c2 = C.objects.create(b=b2) + c3 = C.objects.create(b=b3) + A.objects.create(flag=None, content_object=b1) + A.objects.create(flag=True, content_object=b2) + self.assertQuerysetEqual( + C.objects.filter(b__a__flag=None), + [c1, c3], lambda x: x + ) + self.assertQuerysetEqual( + C.objects.exclude(b__a__flag=None), + [c2], lambda x: x + ) + + def test_ticket_20564_nullable_fk(self): + b1 = B.objects.create() + b2 = B.objects.create() + b3 = B.objects.create() + d1 = D.objects.create(b=b1) + d2 = D.objects.create(b=b2) + d3 = D.objects.create(b=b3) + d4 = D.objects.create() + A.objects.create(flag=None, content_object=b1) + A.objects.create(flag=True, content_object=b1) + A.objects.create(flag=True, content_object=b2) + self.assertQuerysetEqual( + D.objects.exclude(b__a__flag=None), + [d2], lambda x: x + ) + self.assertQuerysetEqual( + D.objects.filter(b__a__flag=None), + [d1, d3, d4], lambda x: x + ) + self.assertQuerysetEqual( + B.objects.filter(a__flag=None), + [b1, b3], lambda x: x + ) + self.assertQuerysetEqual( + B.objects.exclude(a__flag=None), + [b2], lambda x: x + ) + + def test_extra_join_condition(self): + # A crude check that content_type_id is taken in account in the + # join/subquery condition. + self.assertIn("content_type_id", str(B.objects.exclude(a__flag=None).query).lower()) + # No need for any joins - the join from inner query can be trimmed in + # this case (but not in the above case as no a objects at all for given + # B would then fail). + self.assertNotIn(" join ", str(B.objects.exclude(a__flag=True).query).lower()) + self.assertIn("content_type_id", str(B.objects.exclude(a__flag=True).query).lower()) |