summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Lavin <markdlavin@gmail.com>2015-06-05 10:48:57 -0400
committerTim Graham <timograham@gmail.com>2015-06-05 12:23:08 -0400
commit0cfb7ed5c531d3fdb1c4eb79a004c2ea7e7a23e3 (patch)
tree6b7b3ceb96c94a4824a9cdb5cc50f21b4e9a9630
parent469f1e362bb9670b174b37da9edd4631aff7badb (diff)
[1.8.x] Fixed #24924 -- Join promotion for multiple Case expressions
Backport of 541f4ea546ad3065852db816769ba6b584e3f373 from master
-rw-r--r--django/db/models/query_utils.py4
-rw-r--r--docs/releases/1.8.3.txt4
-rw-r--r--tests/expressions_case/tests.py42
3 files changed, 47 insertions, 3 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index 16496b7fad..3f24ab8cf1 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -87,10 +87,8 @@ class Q(tree.Node):
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
# We must promote any new joins to left outer joins so that when Q is
# used as an expression, rows aren't filtered due to joins.
- joins_before = query.tables[:]
clause, joins = query._add_q(self, reuse, allow_joins=allow_joins, split_subq=False)
- joins_to_promote = [j for j in joins if j not in joins_before]
- query.promote_joins(joins_to_promote)
+ query.promote_joins(joins)
return clause
@classmethod
diff --git a/docs/releases/1.8.3.txt b/docs/releases/1.8.3.txt
index d544183445..6ac0d3f592 100644
--- a/docs/releases/1.8.3.txt
+++ b/docs/releases/1.8.3.txt
@@ -53,3 +53,7 @@ Bugfixes
* Fixed queryset annotations when using ``Case`` expressions with ``exclude()``
(:ticket:`24833`).
+
+* Corrected join promotion for multiple ``Case`` expressions. Annotating a
+ query with multiple ``Case`` expressions could unexpectedly filter out
+ results (:ticket:`24924`).
diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py
index 8c081dde9a..d618447833 100644
--- a/tests/expressions_case/tests.py
+++ b/tests/expressions_case/tests.py
@@ -1075,6 +1075,48 @@ class CaseExpressionTests(TestCase):
lambda x: (x, x.foo)
)
+ def test_join_promotion_multiple_annonations(self):
+ o = CaseTestModel.objects.create(integer=1, integer2=1, string='1')
+ # Testing that:
+ # 1. There isn't any object on the remote side of the fk_rel
+ # relation. If the query used inner joins, then the join to fk_rel
+ # would remove o from the results. So, in effect we are testing that
+ # we are promoting the fk_rel join to a left outer join here.
+ # 2. The default value of 3 is generated for the case expression.
+ self.assertQuerysetEqual(
+ CaseTestModel.objects.filter(pk=o.pk).annotate(
+ foo=Case(
+ When(fk_rel__pk=1, then=2),
+ default=3,
+ output_field=models.IntegerField()
+ ),
+ bar=Case(
+ When(fk_rel__pk=1, then=4),
+ default=5,
+ output_field=models.IntegerField()
+ ),
+ ),
+ [(o, 3, 5)],
+ lambda x: (x, x.foo, x.bar)
+ )
+ # Now 2 should be generated, as the fk_rel is null.
+ self.assertQuerysetEqual(
+ CaseTestModel.objects.filter(pk=o.pk).annotate(
+ foo=Case(
+ When(fk_rel__isnull=True, then=2),
+ default=3,
+ output_field=models.IntegerField()
+ ),
+ bar=Case(
+ When(fk_rel__isnull=True, then=4),
+ default=5,
+ output_field=models.IntegerField()
+ ),
+ ),
+ [(o, 2, 4)],
+ lambda x: (x, x.foo, x.bar)
+ )
+
def test_m2m_exclude(self):
CaseTestModel.objects.create(integer=10, integer2=1, string='1')
qs = CaseTestModel.objects.values_list('id', 'integer').annotate(