Fixed #28211 -- Prevented ORing an empty Q() from reducing query join efficiency.

This commit is contained in:
Tom 2017-05-18 19:03:30 +01:00 committed by Tim Graham
parent 1c3a6cec2a
commit bb0b6e5263
2 changed files with 17 additions and 0 deletions

View File

@ -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 large and/or so that they can be used by other modules without getting into
circular import difficulties. circular import difficulties.
""" """
import copy
import functools import functools
import inspect import inspect
from collections import namedtuple from collections import namedtuple
@ -61,6 +62,14 @@ class Q(tree.Node):
def _combine(self, other, conn): def _combine(self, other, conn):
if not isinstance(other, Q): if not isinstance(other, Q):
raise TypeError(other) 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 = type(self)()
obj.connector = conn obj.connector = conn
obj.add(self, conn) obj.add(self, conn)

View File

@ -11,6 +11,14 @@ class QTests(SimpleTestCase):
def test_combine_and_both_empty(self): def test_combine_and_both_empty(self):
self.assertEqual(Q() & Q(), Q()) 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): def test_deconstruct(self):
q = Q(price__gt=F('discounted_price')) q = Q(price__gt=F('discounted_price'))
path, args, kwargs = q.deconstruct() path, args, kwargs = q.deconstruct()