app/django/utils/tree.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
--- a/app/django/utils/tree.py	Tue Oct 14 12:36:55 2008 +0000
+++ b/app/django/utils/tree.py	Tue Oct 14 16:00:59 2008 +0000
@@ -29,6 +29,22 @@
         self.subtree_parents = []
         self.negated = negated
 
+    # We need this because of django.db.models.query_utils.Q. Q. __init__() is
+    # problematic, but it is a natural Node subclass in all other respects.
+    def _new_instance(cls, children=None, connector=None, negated=False):
+        """
+        This is called to create a new instance of this class when we need new
+        Nodes (or subclasses) in the internal code in this class. Normally, it
+        just shadows __init__(). However, subclasses with an __init__ signature
+        that is not an extension of Node.__init__ might need to implement this
+        method to allow a Node to create a new instance of them (if they have
+        any extra setting up to do).
+        """
+        obj = Node(children, connector, negated)
+        obj.__class__ = cls
+        return obj
+    _new_instance = classmethod(_new_instance)
+
     def __str__(self):
         if self.negated:
             return '(NOT (%s: %s))' % (self.connector, ', '.join([str(c) for c
@@ -71,7 +87,7 @@
         Otherwise, the whole tree is pushed down one level and a new root
         connector is created, connecting the existing tree and the new node.
         """
-        if node in self.children:
+        if node in self.children and conn_type == self.connector:
             return
         if len(self.children) < 2:
             self.connector = conn_type
@@ -82,7 +98,8 @@
             else:
                 self.children.append(node)
         else:
-            obj = Node(self.children, self.connector, self.negated)
+            obj = self._new_instance(self.children, self.connector,
+                    self.negated)
             self.connector = conn_type
             self.children = [obj, node]
 
@@ -96,7 +113,8 @@
         Interpreting the meaning of this negate is up to client code. This
         method is useful for implementing "not" arrangements.
         """
-        self.children = [Node(self.children, self.connector, not self.negated)]
+        self.children = [self._new_instance(self.children, self.connector,
+                not self.negated)]
         self.connector = self.default
 
     def start_subtree(self, conn_type):
@@ -108,12 +126,13 @@
         if len(self.children) == 1:
             self.connector = conn_type
         elif self.connector != conn_type:
-            self.children = [Node(self.children, self.connector, self.negated)]
+            self.children = [self._new_instance(self.children, self.connector,
+                    self.negated)]
             self.connector = conn_type
             self.negated = False
 
-        self.subtree_parents.append(Node(self.children, self.connector,
-                self.negated))
+        self.subtree_parents.append(self.__class__(self.children,
+                self.connector, self.negated))
         self.connector = self.default
         self.negated = False
         self.children = []
@@ -126,7 +145,7 @@
         the current instances state to be the parent.
         """
         obj = self.subtree_parents.pop()
-        node = Node(self.children, self.connector)
+        node = self.__class__(self.children, self.connector)
         self.connector = obj.connector
         self.negated = obj.negated
         self.children = obj.children