Fixed bug with QuerySet.exclude() failing to do negations on Q objects, and
at the same time generalised exclude/QNot so that they work for 'external' Q objects i.e. ones that simply have 'get_sql' defined. git-svn-id: http://code.djangoproject.com/svn/django/trunk@2997 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c3baf4668f
commit
60c3e55b1f
|
@ -291,22 +291,26 @@ class QuerySet(object):
|
||||||
|
|
||||||
def filter(self, *args, **kwargs):
|
def filter(self, *args, **kwargs):
|
||||||
"Returns a new QuerySet instance with the args ANDed to the existing set."
|
"Returns a new QuerySet instance with the args ANDed to the existing set."
|
||||||
return self._filter_or_exclude(Q, *args, **kwargs)
|
return self._filter_or_exclude(None, *args, **kwargs)
|
||||||
|
|
||||||
def exclude(self, *args, **kwargs):
|
def exclude(self, *args, **kwargs):
|
||||||
"Returns a new QuerySet instance with NOT (args) ANDed to the existing set."
|
"Returns a new QuerySet instance with NOT (args) ANDed to the existing set."
|
||||||
return self._filter_or_exclude(QNot, *args, **kwargs)
|
return self._filter_or_exclude(QNot, *args, **kwargs)
|
||||||
|
|
||||||
def _filter_or_exclude(self, qtype, *args, **kwargs):
|
def _filter_or_exclude(self, mapper, *args, **kwargs):
|
||||||
|
# mapper is a callable used to transform Q objects,
|
||||||
|
# or None for identity transform
|
||||||
|
if mapper is None:
|
||||||
|
mapper = lambda x: x
|
||||||
if len(args) > 0 or len(kwargs) > 0:
|
if len(args) > 0 or len(kwargs) > 0:
|
||||||
assert self._limit is None and self._offset is None, \
|
assert self._limit is None and self._offset is None, \
|
||||||
"Cannot filter a query once a slice has been taken."
|
"Cannot filter a query once a slice has been taken."
|
||||||
|
|
||||||
clone = self._clone()
|
clone = self._clone()
|
||||||
if len(kwargs) > 0:
|
if len(kwargs) > 0:
|
||||||
clone._filters = clone._filters & qtype(**kwargs)
|
clone._filters = clone._filters & mapper(Q(**kwargs))
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
clone._filters = clone._filters & reduce(operator.and_, args)
|
clone._filters = clone._filters & reduce(operator.and_, map(mapper, args))
|
||||||
return clone
|
return clone
|
||||||
|
|
||||||
def complex_filter(self, filter_obj):
|
def complex_filter(self, filter_obj):
|
||||||
|
@ -318,7 +322,7 @@ class QuerySet(object):
|
||||||
if hasattr(filter_obj, 'get_sql'):
|
if hasattr(filter_obj, 'get_sql'):
|
||||||
return self._filter_or_exclude(None, filter_obj)
|
return self._filter_or_exclude(None, filter_obj)
|
||||||
else:
|
else:
|
||||||
return self._filter_or_exclude(Q, **filter_obj)
|
return self._filter_or_exclude(None, **filter_obj)
|
||||||
|
|
||||||
def select_related(self, true_or_false=True):
|
def select_related(self, true_or_false=True):
|
||||||
"Returns a new QuerySet instance with '_select_related' modified."
|
"Returns a new QuerySet instance with '_select_related' modified."
|
||||||
|
@ -582,9 +586,12 @@ class Q(object):
|
||||||
|
|
||||||
class QNot(Q):
|
class QNot(Q):
|
||||||
"Encapsulates NOT (...) queries as objects"
|
"Encapsulates NOT (...) queries as objects"
|
||||||
|
def __init__(self, q):
|
||||||
|
"Creates a negation of the q object passed in."
|
||||||
|
self.q = q
|
||||||
|
|
||||||
def get_sql(self, opts):
|
def get_sql(self, opts):
|
||||||
tables, joins, where, params = super(QNot, self).get_sql(opts)
|
tables, joins, where, params = self.q.get_sql(opts)
|
||||||
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
where2 = ['(NOT (%s))' % " AND ".join(where)]
|
||||||
return tables, joins, where2, params
|
return tables, joins, where2, params
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,10 @@ Hello and goodbye
|
||||||
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
|
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
|
||||||
{1: Hello}
|
{1: Hello}
|
||||||
|
|
||||||
|
# Demonstrating exclude with a Q object
|
||||||
|
>>> Article.objects.exclude(Q(headline__startswith='Hello'))
|
||||||
|
[Goodbye]
|
||||||
|
|
||||||
# The 'complex_filter' method supports framework features such as
|
# The 'complex_filter' method supports framework features such as
|
||||||
# 'limit_choices_to' which normally take a single dictionary of lookup arguments
|
# 'limit_choices_to' which normally take a single dictionary of lookup arguments
|
||||||
# but need to support arbitrary queries via Q objects too.
|
# but need to support arbitrary queries via Q objects too.
|
||||||
|
|
Loading…
Reference in New Issue