summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2015-05-11 10:02:41 +0300
committerTim Graham <timograham@gmail.com>2015-05-11 11:17:37 -0400
commit056a91dbfae9e741cd4bf033d3062d3d35ff4d90 (patch)
treecaacd938b7f22b75e57c90e67ae007c0b4ceedc2
parentd3a8f36fdb3094e58db24065bc77ec790b95c789 (diff)
[1.8.x] Fixed #24766 -- Added join promotion for Case expressions
Backport of be9d645346a20a6c394bf70d47b1b1d5c81ff530 from master
-rw-r--r--django/db/models/query_utils.py7
-rw-r--r--docs/releases/1.8.2.txt4
-rw-r--r--tests/expressions_case/tests.py32
3 files changed, 42 insertions, 1 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index 10580f77a6..ac9405ec73 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -85,7 +85,12 @@ class Q(tree.Node):
return clone
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
- clause, _ = query._add_q(self, reuse, allow_joins=allow_joins)
+ # 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)
+ joins_to_promote = [j for j in joins if j not in joins_before]
+ query.promote_joins(joins_to_promote)
return clause
@classmethod
diff --git a/docs/releases/1.8.2.txt b/docs/releases/1.8.2.txt
index a0c18e78bb..f430eb6cb0 100644
--- a/docs/releases/1.8.2.txt
+++ b/docs/releases/1.8.2.txt
@@ -13,3 +13,7 @@ Bugfixes
* Fixed crash when reusing the same ``Case`` instance in a query
(:ticket:`24752`).
+
+* Corrected join promotion for ``Case`` expressions. For example, annotating a
+ query with a ``Case`` expression could unexpectedly filter out results
+ (:ticket:`24766`).
diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py
index 5eacd1cf9f..d6ff0debb0 100644
--- a/tests/expressions_case/tests.py
+++ b/tests/expressions_case/tests.py
@@ -1031,6 +1031,38 @@ class CaseExpressionTests(TestCase):
transform=attrgetter('integer', 'test')
)
+ def test_join_promotion(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()
+ ),
+ ),
+ [(o, 3)],
+ lambda x: (x, x.foo)
+ )
+ # 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()
+ ),
+ ),
+ [(o, 2)],
+ lambda x: (x, x.foo)
+ )
+
class CaseDocumentationExamples(TestCase):
@classmethod