Skip to content

Commit

Permalink
Fixed #24924 -- Join promotion for multiple Case expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
mlavin authored and timgraham committed Jun 5, 2015
1 parent f0450c9 commit 541f4ea
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 3 deletions.
4 changes: 1 addition & 3 deletions django/db/models/query_utils.py
Expand Up @@ -89,10 +89,8 @@ def clone(self):
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
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/1.8.3.txt
Expand Up @@ -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`).
42 changes: 42 additions & 0 deletions tests/expressions_case/tests.py
Expand Up @@ -1060,6 +1060,48 @@ def test_join_promotion(self):
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(
Expand Down

0 comments on commit 541f4ea

Please sign in to comment.