From bb0b6e526340e638522e093765e534df4e4393d2 Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 18 May 2017 19:03:30 +0100 Subject: [PATCH] Fixed #28211 -- Prevented ORing an empty Q() from reducing query join efficiency. --- django/db/models/query_utils.py | 9 +++++++++ tests/queries/test_q.py | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 27430ae4ca..0fedfac3e2 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -5,6 +5,7 @@ Factored out from django.db.models.query to avoid making the main module very large and/or so that they can be used by other modules without getting into circular import difficulties. """ +import copy import functools import inspect from collections import namedtuple @@ -61,6 +62,14 @@ class Q(tree.Node): def _combine(self, other, conn): if not isinstance(other, Q): raise TypeError(other) + + # If the other Q() is empty, ignore it and just use `self`. + if not other: + return copy.deepcopy(self) + # Or if this Q is empty, ignore it and just use `other`. + elif not self: + return copy.deepcopy(other) + obj = type(self)() obj.connector = conn obj.add(self, conn) diff --git a/tests/queries/test_q.py b/tests/queries/test_q.py index 7be2361f04..a90d6794db 100644 --- a/tests/queries/test_q.py +++ b/tests/queries/test_q.py @@ -11,6 +11,14 @@ class QTests(SimpleTestCase): def test_combine_and_both_empty(self): self.assertEqual(Q() & Q(), Q()) + def test_combine_or_empty(self): + q = Q(x=1) + self.assertEqual(q | Q(), q) + self.assertEqual(Q() | q, q) + + def test_combine_or_both_empty(self): + self.assertEqual(Q() | Q(), Q()) + def test_deconstruct(self): q = Q(price__gt=F('discounted_price')) path, args, kwargs = q.deconstruct()