Skip to content

Commit 44e29ea

Browse files
committed
[1.11.x] Fixed #28293 -- Fixed union(), intersection(), and difference() when combining with an EmptyQuerySet.
Thanks Jon Dufresne for the report and Tim Graham for the review. Backport of 82175ea from master
1 parent 927d9b5 commit 44e29ea

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

django/db/models/query.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,12 +838,25 @@ def union(self, *other_qs, **kwargs):
838838
"union() received an unexpected keyword argument '%s'" %
839839
(unexpected_kwarg,)
840840
)
841+
# If the query is an EmptyQuerySet, combine all nonempty querysets.
842+
if isinstance(self, EmptyQuerySet):
843+
qs = [q for q in other_qs if not isinstance(q, EmptyQuerySet)]
844+
return qs[0]._combinator_query('union', *qs[1:], **kwargs) if qs else self
841845
return self._combinator_query('union', *other_qs, **kwargs)
842846

843847
def intersection(self, *other_qs):
848+
# If any query is an EmptyQuerySet, return it.
849+
if isinstance(self, EmptyQuerySet):
850+
return self
851+
for other in other_qs:
852+
if isinstance(other, EmptyQuerySet):
853+
return other
844854
return self._combinator_query('intersection', *other_qs)
845855

846856
def difference(self, *other_qs):
857+
# If the query is an EmptyQuerySet, return it.
858+
if isinstance(self, EmptyQuerySet):
859+
return self
847860
return self._combinator_query('difference', *other_qs)
848861

849862
def select_for_update(self, nowait=False, skip_locked=False):

django/db/models/sql/compiler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def get_combinator_sql(self, combinator, all):
379379
features = self.connection.features
380380
compilers = [
381381
query.get_compiler(self.using, self.connection)
382-
for query in self.query.combined_queries
382+
for query in self.query.combined_queries if not query.is_empty()
383383
]
384384
if not features.supports_slicing_ordering_in_compound:
385385
for query, compiler in zip(self.query.combined_queries, compilers):

docs/releases/1.11.3.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ Bugfixes
2929

3030
* Fixed crash in admin's inlines when a model has an inherited non-editable
3131
primary key (:ticket:`27967`).
32+
33+
* Fixed ``QuerySet.union()``, ``intersection()``, and ``difference()`` when
34+
combining with an ``EmptyQuerySet`` (:ticket:`28293`).

tests/queries/test_qs_combinators.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,31 @@ def test_union_distinct(self):
4545
self.assertEqual(len(list(qs1.union(qs2, all=True))), 20)
4646
self.assertEqual(len(list(qs1.union(qs2))), 10)
4747

48+
@skipUnlessDBFeature('supports_select_intersection')
49+
def test_intersection_with_empty_qs(self):
50+
qs1 = Number.objects.all()
51+
qs2 = Number.objects.none()
52+
self.assertEqual(len(qs1.intersection(qs2)), 0)
53+
self.assertEqual(len(qs2.intersection(qs1)), 0)
54+
self.assertEqual(len(qs2.intersection(qs2)), 0)
55+
56+
@skipUnlessDBFeature('supports_select_difference')
57+
def test_difference_with_empty_qs(self):
58+
qs1 = Number.objects.all()
59+
qs2 = Number.objects.none()
60+
self.assertEqual(len(qs1.difference(qs2)), 10)
61+
self.assertEqual(len(qs2.difference(qs1)), 0)
62+
self.assertEqual(len(qs2.difference(qs2)), 0)
63+
64+
def test_union_with_empty_qs(self):
65+
qs1 = Number.objects.all()
66+
qs2 = Number.objects.none()
67+
self.assertEqual(len(qs1.union(qs2)), 10)
68+
self.assertEqual(len(qs2.union(qs1)), 10)
69+
self.assertEqual(len(qs2.union(qs1, qs1, qs1)), 10)
70+
self.assertEqual(len(qs2.union(qs1, qs1, all=True)), 20)
71+
self.assertEqual(len(qs2.union(qs2)), 0)
72+
4873
def test_union_bad_kwarg(self):
4974
qs1 = Number.objects.all()
5075
msg = "union() received an unexpected keyword argument 'bad'"

0 commit comments

Comments
 (0)