summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-02-28 02:49:31 +0000
committerMalcolm Tredinnick <malcolm.tredinnick@gmail.com>2008-02-28 02:49:31 +0000
commit0588dee34cde61f408e06766d95c2a3a882883bd (patch)
tree5a4acda407b991ba7727ad10f7d0a107e0f27fa6
parentc8afb75d53a0fc1a4c8be408b0963076b1925536 (diff)
queryset-refactor: Fixed a few really silly errors in the Q class and negation
handling in the tree class. Discovered whilst starting to fix exclude(). git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7169 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r--django/db/models/query.py13
-rw-r--r--django/db/models/query_utils.py26
-rw-r--r--django/db/models/sql/where.py6
-rw-r--r--django/utils/tree.py45
4 files changed, 45 insertions, 45 deletions
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 195bbff91b..c686c51ea2 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -2,7 +2,7 @@ import warnings
from django.db import connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist
-from django.db.models.query_utils import Q, QNot, EmptyResultSet
+from django.db.models.query_utils import Q, EmptyResultSet, not_q
from django.db.models import signals, sql
from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict
@@ -316,7 +316,7 @@ class _QuerySet(object):
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(not_q, *args, **kwargs)
def _filter_or_exclude(self, mapper, *args, **kwargs):
# mapper is a callable used to transform Q objects,
@@ -591,15 +591,20 @@ class EmptyQuerySet(QuerySet):
# (it raises StopIteration immediately).
yield iter([]).next()
-# QOperator, QAnd and QOr are temporarily retained for backwards compatibility.
-# All the old functionality is now part of the 'Q' class.
+# QOperator, QNot, QAnd and QOr are temporarily retained for backwards
+# compatibility. All the old functionality is now part of the 'Q' class.
class QOperator(Q):
def __init__(self, *args, **kwargs):
warnings.warn('Use Q instead of QOr, QAnd or QOperation.',
DeprecationWarning, stacklevel=2)
+ super(QOperator, self).__init__(*args, **kwargs)
QOr = QAnd = QOperator
+def QNot(q):
+ warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2)
+ return ~q
+
def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,
requested=None):
"""Helper function that recursively returns an object with cache filled"""
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index 9782a4bd7a..426811be89 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -4,6 +4,9 @@ Various data structures used in query construction.
Factored out from django.db.models.query so that they can also be used by other
modules without getting into circular import difficulties.
"""
+
+from copy import deepcopy
+
from django.utils import tree
class EmptyResultSet(Exception):
@@ -31,8 +34,9 @@ class Q(tree.Node):
def _combine(self, other, conn):
if not isinstance(other, Q):
raise TypeError(other)
- self.add(other, conn)
- return self
+ obj = deepcopy(self)
+ obj.add(other, conn)
+ return obj
def __or__(self, other):
return self._combine(other, self.OR)
@@ -41,18 +45,10 @@ class Q(tree.Node):
return self._combine(other, self.AND)
def __invert__(self):
- return QNot(self)
-
-class QNot(Q):
- """
- Encapsulates the negation of a Q object.
- """
- def __init__(self, q):
- """Creates the negation of the Q object passed in."""
- super(QNot, self).__init__()
- self.add(q, self.AND)
- self.negate()
+ obj = deepcopy(self)
+ obj.negate()
+ return obj
- def __invert__(self):
- return self.children[0]
+def not_q(q):
+ return ~q
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 16cfdb21b0..e699e96375 100644
--- a/django/db/models/sql/where.py
+++ b/django/db/models/sql/where.py
@@ -49,10 +49,12 @@ class WhereNode(tree.Node):
format = '(%s)'
elif isinstance(child, tree.Node):
sql, params = self.as_sql(child, qn)
- if child.negated:
- format = 'NOT (%s)'
+ if len(child.children) == 1:
+ format = '%s'
else:
format = '(%s)'
+ if child.negated:
+ format = 'NOT %s' % format
else:
sql, params = self.make_atom(child, qn)
format = '%s'
diff --git a/django/utils/tree.py b/django/utils/tree.py
index b3a0e06812..0d12ea1cbc 100644
--- a/django/utils/tree.py
+++ b/django/utils/tree.py
@@ -3,7 +3,7 @@ A class for storing a tree graph. Primarily used for filter constructs in the
ORM.
"""
-import copy
+from copy import deepcopy
class Node(object):
"""
@@ -15,24 +15,27 @@ class Node(object):
# subclasses will usually override the value.
default = 'DEFAULT'
- def __init__(self, children=None, connector=None):
+ def __init__(self, children=None, connector=None, negated=False):
self.children = children and children[:] or []
self.connector = connector or self.default
self.subtree_parents = []
- self.negated = False
+ self.negated = negated
def __str__(self):
+ if self.negated:
+ return '(NOT (%s: %s))' % (self.connector, ', '.join([str(c) for c
+ in self.children]))
return '(%s: %s)' % (self.connector, ', '.join([str(c) for c in
- self.children]))
+ self.children]))
def __deepcopy__(self, memodict):
"""
Utility method used by copy.deepcopy().
"""
- obj = self.__class__(connector=self.connector)
- obj.children = copy.deepcopy(self.children, memodict)
- obj.subtree_parents = copy.deepcopy(self.subtree_parents, memodict)
- obj.negated = self.negated
+ obj = Node(connector=self.connector, negated=self.negated)
+ obj.__class__ = self.__class__
+ obj.children = deepcopy(self.children, memodict)
+ obj.subtree_parents = deepcopy(self.subtree_parents, memodict)
return obj
def __len__(self):
@@ -63,13 +66,13 @@ class Node(object):
if len(self.children) < 2:
self.connector = conn_type
if self.connector == conn_type:
- if isinstance(node, Node) and (node.connector == conn_type
- or len(node) == 1):
+ if isinstance(node, Node) and (node.connector == conn_type or
+ len(node) == 1):
self.children.extend(node.children)
else:
self.children.append(node)
else:
- obj = Node(self.children, self.connector)
+ obj = Node(self.children, self.connector, self.negated)
self.connector = conn_type
self.children = [obj, node]
@@ -80,8 +83,7 @@ class Node(object):
Interpreting the meaning of this negate is up to client code. This
method is useful for implementing "not" arrangements.
"""
- self.children = [NegatedNode(self.children, self.connector,
- old_state=self.negated)]
+ self.children = [Node(self.children, self.connector, not self.negated)]
self.connector = self.default
def start_subtree(self, conn_type):
@@ -93,11 +95,14 @@ class Node(object):
if len(self.children) == 1:
self.connector = conn_type
elif self.connector != conn_type:
- self.children = [Node(self.children, self.connector)]
+ self.children = [Node(self.children, self.connector, self.negated)]
self.connector = conn_type
+ self.negated = False
- self.subtree_parents.append(Node(self.children, self.connector))
+ self.subtree_parents.append(Node(self.children, self.connector,
+ self.negated))
self.connector = self.default
+ self.negated = False
self.children = []
def end_subtree(self):
@@ -110,15 +115,7 @@ class Node(object):
obj = self.subtree_parents.pop()
node = Node(self.children, self.connector)
self.connector = obj.connector
+ self.negated = obj.negated
self.children = obj.children
self.children.append(node)
-class NegatedNode(Node):
- """
- A class that indicates the connector type should be negated (whatever that
- means -- it's up to the client) when used by the client code.
- """
- def __init__(self, children=None, connector=None, old_state=True):
- super(NegatedNode, self).__init__(children, connector)
- self.negated = not old_state
-