From 05e4d51f0d1924efec47c39215d6baf682688fe7 Mon Sep 17 00:00:00 2001 From: Malcolm Tredinnick Date: Fri, 25 Apr 2008 15:02:25 +0000 Subject: [PATCH] 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 --- django/db/models/query.py | 22 +++++++++------------- django/db/models/query_utils.py | 8 +------- django/db/models/sql/query.py | 19 ++++--------------- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index 5a501bb09d..1bafa64805 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -3,7 +3,7 @@ import warnings from django.conf import settings from django.db import connection, transaction, IntegrityError 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.dispatch import dispatcher 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 set. """ - return self._filter_or_exclude(None, *args, **kwargs) + return self._filter_or_exclude(False, *args, **kwargs) def exclude(self, *args, **kwargs): """ Returns a new QuerySet instance with NOT (args) ANDed to the existing 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): - # mapper is a callable used to transform Q objects, - # or None for identity transform. - if mapper is None: - mapper = lambda x: x + def _filter_or_exclude(self, negate, *args, **kwargs): if args or kwargs: 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() - if kwargs: - clone.query.add_q(mapper(Q(**kwargs))) - for arg in args: - clone.query.add_q(mapper(arg)) + if negate: + clone.query.add_q(~Q(*args, **kwargs)) + else: + clone.query.add_q(Q(*args, **kwargs)) return clone def complex_filter(self, filter_obj): diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 6f1b860f21..0ce7900c74 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -28,10 +28,7 @@ class Q(tree.Node): default = AND def __init__(self, *args, **kwargs): - if args and kwargs: - raise TypeError('Use positional *or* kwargs; not both!') - nodes = list(args) + kwargs.items() - super(Q, self).__init__(children=nodes) + super(Q, self).__init__(children=list(args) + kwargs.items()) def _combine(self, other, conn): if not isinstance(other, Q): @@ -51,6 +48,3 @@ class Q(tree.Node): obj.negate() return obj -def not_q(q): - return ~q - diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 864563ed8e..f431df332f 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -969,24 +969,11 @@ class Query(object): # that's harmless. self.promote_alias(table) - entry = (alias, col, field, lookup_type, value) - 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 - + self.where.add((alias, col, field, lookup_type, value), connector) if negate: + self.where.negate() for alias in join_list: self.promote_alias(alias) - if not merged: - self.where.negate() if final > 1 and lookup_type != 'isnull': for alias in join_list: if self.alias_map[alias] == self.LOUTER: @@ -1019,6 +1006,8 @@ class Query(object): self.where.start_subtree(connector) self.add_q(child) self.where.end_subtree() + if q_object.negated: + self.where.children[-1].negate() else: self.add_filter(child, connector, q_object.negated, single_filter=internal)