summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2017-06-13 08:16:16 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2017-06-13 08:33:26 +0200
commit44e29ea1e906859e85bb2a46ae5ea9d82bd96f5f (patch)
treebc22ec611fad767850afc27d4dc8c6f7874d891e
parent927d9b51fee2442280ae975b21b98b5a705c4b17 (diff)
[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 82175ead723f8fa3f9271fbd4b24275097029aab from master
-rw-r--r--django/db/models/query.py13
-rw-r--r--django/db/models/sql/compiler.py2
-rw-r--r--docs/releases/1.11.3.txt3
-rw-r--r--tests/queries/test_qs_combinators.py25
4 files changed, 42 insertions, 1 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index c9ff437232..8a97e45b88 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -838,12 +838,25 @@ class QuerySet(object):
"union() received an unexpected keyword argument '%s'" %
(unexpected_kwarg,)
)
+ # If the query is an EmptyQuerySet, combine all nonempty querysets.
+ if isinstance(self, EmptyQuerySet):
+ qs = [q for q in other_qs if not isinstance(q, EmptyQuerySet)]
+ return qs[0]._combinator_query('union', *qs[1:], **kwargs) if qs else self
return self._combinator_query('union', *other_qs, **kwargs)
def intersection(self, *other_qs):
+ # If any query is an EmptyQuerySet, return it.
+ if isinstance(self, EmptyQuerySet):
+ return self
+ for other in other_qs:
+ if isinstance(other, EmptyQuerySet):
+ return other
return self._combinator_query('intersection', *other_qs)
def difference(self, *other_qs):
+ # If the query is an EmptyQuerySet, return it.
+ if isinstance(self, EmptyQuerySet):
+ return self
return self._combinator_query('difference', *other_qs)
def select_for_update(self, nowait=False, skip_locked=False):
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py
index 9acb56aa84..d1373fcf95 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -379,7 +379,7 @@ class SQLCompiler(object):
features = self.connection.features
compilers = [
query.get_compiler(self.using, self.connection)
- for query in self.query.combined_queries
+ for query in self.query.combined_queries if not query.is_empty()
]
if not features.supports_slicing_ordering_in_compound:
for query, compiler in zip(self.query.combined_queries, compilers):
diff --git a/docs/releases/1.11.3.txt b/docs/releases/1.11.3.txt
index e742f3e770..33645d05b3 100644
--- a/docs/releases/1.11.3.txt
+++ b/docs/releases/1.11.3.txt
@@ -29,3 +29,6 @@ Bugfixes
* Fixed crash in admin's inlines when a model has an inherited non-editable
primary key (:ticket:`27967`).
+
+* Fixed ``QuerySet.union()``, ``intersection()``, and ``difference()`` when
+ combining with an ``EmptyQuerySet`` (:ticket:`28293`).
diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py
index a0faab2eb7..ec341952ea 100644
--- a/tests/queries/test_qs_combinators.py
+++ b/tests/queries/test_qs_combinators.py
@@ -45,6 +45,31 @@ class QuerySetSetOperationTests(TestCase):
self.assertEqual(len(list(qs1.union(qs2, all=True))), 20)
self.assertEqual(len(list(qs1.union(qs2))), 10)
+ @skipUnlessDBFeature('supports_select_intersection')
+ def test_intersection_with_empty_qs(self):
+ qs1 = Number.objects.all()
+ qs2 = Number.objects.none()
+ self.assertEqual(len(qs1.intersection(qs2)), 0)
+ self.assertEqual(len(qs2.intersection(qs1)), 0)
+ self.assertEqual(len(qs2.intersection(qs2)), 0)
+
+ @skipUnlessDBFeature('supports_select_difference')
+ def test_difference_with_empty_qs(self):
+ qs1 = Number.objects.all()
+ qs2 = Number.objects.none()
+ self.assertEqual(len(qs1.difference(qs2)), 10)
+ self.assertEqual(len(qs2.difference(qs1)), 0)
+ self.assertEqual(len(qs2.difference(qs2)), 0)
+
+ def test_union_with_empty_qs(self):
+ qs1 = Number.objects.all()
+ qs2 = Number.objects.none()
+ self.assertEqual(len(qs1.union(qs2)), 10)
+ self.assertEqual(len(qs2.union(qs1)), 10)
+ self.assertEqual(len(qs2.union(qs1, qs1, qs1)), 10)
+ self.assertEqual(len(qs2.union(qs1, qs1, all=True)), 20)
+ self.assertEqual(len(qs2.union(qs2)), 0)
+
def test_union_bad_kwarg(self):
qs1 = Number.objects.all()
msg = "union() received an unexpected keyword argument 'bad'"