diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index c913267476..1623263964 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -305,6 +305,7 @@ class Query(BaseExpression): obj.annotation_select_mask = None else: obj.annotation_select_mask = self.annotation_select_mask.copy() + obj.combined_queries = tuple(query.clone() for query in self.combined_queries) # _annotation_select_cache cannot be copied, as doing so breaks the # (necessary) state in which both annotations and # _annotation_select_cache point to the same underlying objects. @@ -1777,6 +1778,8 @@ class Query(BaseExpression): def set_empty(self): self.where.add(NothingNode(), AND) + for query in self.combined_queries: + query.set_empty() def is_empty(self): return any(isinstance(c, NothingNode) for c in self.where.children) diff --git a/tests/queries/test_qs_combinators.py b/tests/queries/test_qs_combinators.py index 9c6fd474ca..75e2aa2f39 100644 --- a/tests/queries/test_qs_combinators.py +++ b/tests/queries/test_qs_combinators.py @@ -51,6 +51,13 @@ class QuerySetSetOperationTests(TestCase): self.assertEqual(len(list(qs1.union(qs2, all=True))), 20) self.assertEqual(len(list(qs1.union(qs2))), 10) + def test_union_none(self): + qs1 = Number.objects.filter(num__lte=1) + qs2 = Number.objects.filter(num__gte=8) + qs3 = qs1.union(qs2) + self.assertSequenceEqual(qs3.none(), []) + self.assertNumbersEqual(qs3, [0, 1, 8, 9], ordered=False) + @skipUnlessDBFeature('supports_select_intersection') def test_intersection_with_empty_qs(self): qs1 = Number.objects.all()