[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 82175ead72 from master
This commit is contained in:
Mariusz Felisiak 2017-06-13 08:16:16 +02:00
parent 927d9b51fe
commit 44e29ea1e9
4 changed files with 42 additions and 1 deletions

View File

@ -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):

View File

@ -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):

View File

@ -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`).

View File

@ -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'"