1
0
mirror of https://github.com/django/django.git synced 2025-07-06 10:49:17 +00:00

queryset-refactor: Simplify the way filters are passed to the Query class.

This removes a lot of the complexity for handling exclude() calls and results
in more efficient code. I feel a bit stupid for not having spotted this earlier.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7461 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-04-25 15:02:25 +00:00
parent fcc3648b60
commit 05e4d51f0d
3 changed files with 14 additions and 35 deletions

View File

@ -3,7 +3,7 @@ import warnings
from django.conf import settings from django.conf import settings
from django.db import connection, transaction, IntegrityError from django.db import connection, transaction, IntegrityError
from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.query_utils import Q, not_q from django.db.models.query_utils import Q
from django.db.models import signals, sql from django.db.models import signals, sql
from django.dispatch import dispatcher from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
@ -355,29 +355,25 @@ class QuerySet(object):
Returns a new QuerySet instance with the args ANDed to the existing Returns a new QuerySet instance with the args ANDed to the existing
set. set.
""" """
return self._filter_or_exclude(None, *args, **kwargs) return self._filter_or_exclude(False, *args, **kwargs)
def exclude(self, *args, **kwargs): def exclude(self, *args, **kwargs):
""" """
Returns a new QuerySet instance with NOT (args) ANDed to the existing Returns a new QuerySet instance with NOT (args) ANDed to the existing
set. set.
""" """
return self._filter_or_exclude(not_q, *args, **kwargs) return self._filter_or_exclude(True, *args, **kwargs)
def _filter_or_exclude(self, mapper, *args, **kwargs): def _filter_or_exclude(self, negate, *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 args or kwargs: if args or kwargs:
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"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 kwargs: if negate:
clone.query.add_q(mapper(Q(**kwargs))) clone.query.add_q(~Q(*args, **kwargs))
for arg in args: else:
clone.query.add_q(mapper(arg)) clone.query.add_q(Q(*args, **kwargs))
return clone return clone
def complex_filter(self, filter_obj): def complex_filter(self, filter_obj):

View File

@ -28,10 +28,7 @@ class Q(tree.Node):
default = AND default = AND
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if args and kwargs: super(Q, self).__init__(children=list(args) + kwargs.items())
raise TypeError('Use positional *or* kwargs; not both!')
nodes = list(args) + kwargs.items()
super(Q, self).__init__(children=nodes)
def _combine(self, other, conn): def _combine(self, other, conn):
if not isinstance(other, Q): if not isinstance(other, Q):
@ -51,6 +48,3 @@ class Q(tree.Node):
obj.negate() obj.negate()
return obj return obj
def not_q(q):
return ~q

View File

@ -969,24 +969,11 @@ class Query(object):
# that's harmless. # that's harmless.
self.promote_alias(table) self.promote_alias(table)
entry = (alias, col, field, lookup_type, value) self.where.add((alias, col, field, lookup_type, value), connector)
if negate and single_filter:
# This case is when we're doing the Q2 filter in exclude(Q1, Q2).
# It's different from exclude(Q1).exclude(Q2).
for node in self.where.children:
if getattr(node, 'negated', False):
node.add(entry, connector)
merged = True
break
else:
self.where.add(entry, connector)
merged = False
if negate: if negate:
self.where.negate()
for alias in join_list: for alias in join_list:
self.promote_alias(alias) self.promote_alias(alias)
if not merged:
self.where.negate()
if final > 1 and lookup_type != 'isnull': if final > 1 and lookup_type != 'isnull':
for alias in join_list: for alias in join_list:
if self.alias_map[alias] == self.LOUTER: if self.alias_map[alias] == self.LOUTER:
@ -1019,6 +1006,8 @@ class Query(object):
self.where.start_subtree(connector) self.where.start_subtree(connector)
self.add_q(child) self.add_q(child)
self.where.end_subtree() self.where.end_subtree()
if q_object.negated:
self.where.children[-1].negate()
else: else:
self.add_filter(child, connector, q_object.negated, self.add_filter(child, connector, q_object.negated,
single_filter=internal) single_filter=internal)