summaryrefslogtreecommitdiff
path: root/tests/generic_relations_regress
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2013-06-06 00:29:44 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2013-06-06 01:54:46 +0300
commit31fd64ad8a98d7de0acb9144ae6f7bd124700cb0 (patch)
treeb3c8fa23035de4e88ceab1e21afee6f60c40052a /tests/generic_relations_regress
parent8c5b805ca11ff25ffa8a4efd0781efe5cc872de3 (diff)
downloaddjango-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.py24
-rw-r--r--tests/generic_relations_regress/tests.py58
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())