angri avatar angri committed f491830

moved code around a bit, documented private methods. no code change this time around

Comments (0)

Files changed (1)

sqlamp/__init__.py

                                opts.pk_field == node_id)
         )
         assert old_parent_id, "Node %s is already a root of own tree" % node_id
+
+        # new tree will have next available tree_id
         new_tree_id = session.execute(sqlalchemy.select([
             sqlalchemy.func.max(opts.tree_id_field) + 1
         ])).scalar()
         """
         self._move_subtree_by_sibling('after', session, node_id, anchor_id)
 
+    def _move_subtree_by_sibling(self, before_or_after, session,
+                                 node_id, anchor_id):
+        """
+        The common code for :meth:`move_subtree_before`
+        and :meth:`move_subtree_after`.
+
+        :param before_or_after: string, either 'before' or 'after'.
+        """
+        opts = self._mp_opts
+        old_path, old_depth, old_tree_id, \
+            new_parent_id, anchors_path, new_depth, new_tree_id \
+            = self._prepare_to_move_subtree(session, node_id, anchor_id)
+
+        # We do not impose ordering of distinct trees, so even though
+        # technically roots of different trees are siblings, changing
+        # they order with "move_subtree_(before|after)" is not supported.
+        assert new_parent_id is not None, \
+               "Use detach_subtree() for creating a new distinct tree"
+
+        if before_or_after == 'after':
+            anchors_path = inc_path(anchors_path, opts.steplen)
+        else:
+            assert before_or_after == 'before'
+
+        # Freeing a place for target node.
+        self._pull_nodes('down', session, new_tree_id, anchors_path, new_depth)
+        # Target node could be thw following sibling or to be the descendant
+        # of one of following siblings. In that case its path has been updated
+        # on the previous step, so we need to fetch it again. If the target
+        # node belongs to different tree or to one of previous siblings,
+        # this query is redundant, but harmless.
+        [[old_path]] = session.execute(
+            sqlalchemy.select([opts.path_field], opts.pk_field == node_id)
+        )
+        new_path = anchors_path
+        self._reparent(session, node_id, new_parent_id, new_tree_id, new_path,
+                       new_depth, old_tree_id, old_path, old_depth)
+
     def move_subtree_to_top(self, session, node_id, new_parent_id):
         """
         Move tree/subtree starting from ``node_id`` to make it the first child
         new_depth = parents_depth + 1
 
         new_path = parents_path + ALPHABET[0] * opts.steplen
+        # Pulling down all new parent's children.
         self._pull_nodes('down', session, new_tree_id, new_path, new_depth)
+        # Updating target node's path (see _move_subtree_by_sibling).
         [[old_path]] = session.execute(
             sqlalchemy.select([opts.path_field], opts.pk_field == node_id)
         )
                       .limit(1)
         ).fetchall()
         if not last_child_path:
+            # The new parent doesn't have any child nodes.
+            # Target node will be the first.
             new_path = parents_path + ALPHABET[0] * opts.steplen
         else:
+            # Target node path will be the next after last child.
             [[last_child_path]] = last_child_path
             try:
                 new_path = inc_path(last_child_path, opts.steplen)
         self._reparent(session, node_id, new_parent_id, new_tree_id, new_path,
                        new_depth, old_tree_id, old_path, old_depth)
 
-    def _move_subtree_by_sibling(self, before_or_after, session,
-                                 node_id, anchor_id):
-        opts = self._mp_opts
-        old_path, old_depth, old_tree_id, \
-            new_parent_id, anchors_path, new_depth, new_tree_id \
-            = self._prepare_to_move_subtree(session, node_id, anchor_id)
+    def _prepare_to_move_subtree(self, session, node_id, anchor_id):
+        """
+        Fetch target and anchor nodes data and check that moving operation
+        is valid for that pair of nodes.
 
-        assert new_parent_id is not None, \
-               "Use detach_subtree() for creating a new distinct tree"
-
-        if before_or_after == 'after':
-            anchors_path = inc_path(anchors_path, opts.steplen)
-        else:
-            assert before_or_after == 'before'
-
-        self._pull_nodes('down', session, new_tree_id, anchors_path, new_depth)
-        [[old_path]] = session.execute(
-            sqlalchemy.select([opts.path_field], opts.pk_field == node_id)
-        )
-        new_path = anchors_path
-        self._reparent(session, node_id, new_parent_id, new_tree_id, new_path,
-                       new_depth, old_tree_id, old_path, old_depth)
-
-    def _prepare_to_move_subtree(self, session, node_id, anchor_id):
+        :raises MovingToDescendantError:
+            If anchor node is descendant of target node.
+        :returns:
+            7-element tuple with the path, depth, tree_id of target node
+            and parent_id, path, depth, tree_id of anchor node.
+        """
         opts = self._mp_opts
         columns = [opts.parent_id_field, opts.path_field,
                    opts.depth_field, opts.tree_id_field]
 
     def _reparent(self, session, node_id, new_parent_id, new_tree_id, new_path,
                   new_depth, old_tree_id, old_path, old_depth):
+        """
+        Update node's parent_id , then :meth:`_update_subtree` and then fill
+        the gap left from moving a subtree to a new place by pulling following
+        siblings up.
+        """
         opts = self._mp_opts
         session.execute(
             opts.table.update().where(opts.pk_field == node_id) \
 
     def _update_subtree(self, session, node_id, new_tree_id, new_path,
                         new_depth, old_tree_id, old_path, old_depth):
+        """
+        Update subtree (starting from node ``node_id``) nodes' depth, path
+        and tree_id.
+
+        The method doesn't deal with recalculating paths, it only can cut
+        and/or concatenate them.
+        """
         opts = self._mp_opts
         descendants_filter = opts.filter_descendants(old_tree_id, old_path,
                                                      and_self=True)
         depth_delta = new_depth - old_depth
+        # Will use updates with sql expressions
         new_depth = opts.depth_field + depth_delta
         new_path_expr = sqlalchemy.func.substr(
             opts.path_field, opts.steplen * old_depth + 1
         )
 
     def _pull_nodes(self, up_or_down, session, tree_id, from_path, depth):
+        """
+        Move all nodes in tree ``tree_id`` starting from path ``from_path``
+        with depth ``depth`` and same parent as node with path ``from_path``
+        either one step up or one step down.
+
+        This is used to free a space for node which is been moving to land
+        (moving down) as well as to remove a gap from that node's old place
+        (moving up).
+        """
         if depth == 0:
             # roots have no siblings, nothing to pull in either direction
             return
             sqlalchemy.select([opts.pk_field, opts.path_field], filter_) \
                       .order_by(opts.tree_id_field, opts.path_field)
         )
+        # We can't do path math at sql side without resorting to DBMS-specific
+        # things or to using stored procedures. Therefore we have to process
+        # siblings sequentially.
         if up_or_down == 'down':
+            # Moving several nodes in a row down has to be done
+            # in backwards direction.
             nodes = list(reversed(nodes.fetchall()))
             if not nodes:
                 return
             try:
                 prev_path = inc_path(lastnodepath, opts.steplen)
             except PathOverflowError:
+                # The last sibling is the last possible node.
                 raise TooManyChildrenError()
         else:
             assert up_or_down == 'up'
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.