def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. """ pos = self._prepare_pos_var_for_move(pos) sib_order = None parent = None if pos in ('first-child', 'last-child', 'sorted-child'): # moving to a child if not target.is_leaf(): target = target.get_last_child() pos = { 'first-child': 'first-sibling', 'last-child': 'last-sibling', 'sorted-child': 'sorted-sibling' }[pos] else: parent = target if pos == 'sorted-child': pos = 'sorted-sibling' else: pos = 'first-sibling' sib_order = 1 if target.is_descendant_of(self): raise InvalidMoveToDescendant( _("Can't move node to a descendant.")) if self == target and ( (pos == 'left') or (pos in ('right', 'last-sibling') and target == target.get_last_sibling()) or (pos == 'first-sibling' and target == target.get_first_sibling())): # special cases, not actually moving the node so no need to UPDATE return if pos == 'sorted-sibling': if parent: self.parent = parent else: self.parent = target.parent else: if sib_order: self.sib_order = sib_order else: self.sib_order = self.__class__._get_new_sibling_order( pos, target) if parent: self.parent = parent else: self.parent = target.parent self.save() transaction.commit_unless_managed()
def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. :raise PathOverflow: when the library can't make room for the node's new position """ pos = self._prepare_pos_var_for_move(pos) oldpath = self.path # initialize variables and if moving to a child, updates "move to # child" to become a "move to sibling" if possible (if it can't # be done, it means that we are adding the first child) (pos, target, newdepth, siblings, newpos) = (self._fix_move_to_child(pos, target)) if target.is_descendant_of(self): raise InvalidMoveToDescendant( _("Can't move node to a descendant.")) if oldpath == target.path and ( (pos == 'left') or (pos in ('right', 'last-sibling') and target.path == target.get_last_sibling().path) or (pos == 'first-sibling' and target.path == target.get_first_sibling().path)): # special cases, not actually moving the node so no need to UPDATE return if pos == 'sorted-sibling': siblings = self.get_sorted_pos_queryset(target.get_siblings(), self) try: newpos = self._get_lastpos_in_path(siblings.all()[0].path) except IndexError: newpos = None if newpos is None: pos = 'last-sibling' stmts = [] # generate the sql that will do the actual moving of nodes oldpath, newpath = self._move_add_sibling_aux(pos, newpos, newdepth, target, siblings, stmts, oldpath, True) # updates needed for mysql and children count in parents self._updates_after_move(oldpath, newpath, stmts) cursor = self._get_database_cursor('write') for sql, vals in stmts: cursor.execute(sql, vals) transaction.commit_unless_managed()
def process(self): self.pos = self.node._prepare_pos_var_for_move(self.pos) oldpath = self.node.path # initialize variables and if moving to a child, updates "move to # child" to become a "move to sibling" if possible (if it can't # be done, it means that we are adding the first child) newdepth, siblings, newpos = self.update_move_to_child_vars() if self.target.is_descendant_of(self.node): raise InvalidMoveToDescendant( _("Can't move node to a descendant.")) if ( oldpath == self.target.path and ( (self.pos == 'left') or ( self.pos in ('right', 'last-sibling') and self.target.path == self.target.get_last_sibling().path ) or ( self.pos == 'first-sibling' and self.target.path == self.target.get_first_sibling().path ) ) ): # special cases, not actually moving the node so no need to UPDATE return if self.pos == 'sorted-sibling': siblings = self.node.get_sorted_pos_queryset( self.target.get_siblings(), self.node) try: newpos = siblings.all()[0]._get_lastpos_in_path() except IndexError: newpos = None if newpos is None: self.pos = 'last-sibling' # generate the sql that will do the actual moving of nodes oldpath, newpath = self.reorder_nodes_before_add_or_move( self.pos, newpos, newdepth, self.target, siblings, oldpath, True) # updates needed for mysql and children count in parents self.sanity_updates_after_move(oldpath, newpath) self.run_sql_stmts()
def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. """ pos = self._prepare_pos_var_for_move(pos) cls = self.__class__ parent = None if pos in ('first-child', 'last-child', 'sorted-child'): # moving to a child if target.is_leaf(): parent = target pos = 'last-child' else: target = target.get_last_child() pos = { 'first-child': 'first-sibling', 'last-child': 'last-sibling', 'sorted-child': 'sorted-sibling' }[pos] if target.is_descendant_of(self): raise InvalidMoveToDescendant( _("Can't move node to a descendant.")) if self == target and ( (pos == 'left') or (pos in ('right', 'last-sibling') and target == target.get_last_sibling()) or (pos == 'first-sibling' and target == target.get_first_sibling())): # special cases, not actually moving the node so no need to UPDATE return if pos == 'sorted-sibling': siblings = list( target.get_sorted_pos_queryset(target.get_siblings(), self)) if siblings: pos = 'left' target = siblings[0] else: pos = 'last-sibling' if pos in ('left', 'right', 'first-sibling'): siblings = list(target.get_siblings()) if pos == 'right': if target == siblings[-1]: pos = 'last-sibling' else: pos = 'left' found = False for node in siblings: if found: target = node break elif node == target: found = True if pos == 'left': if target == siblings[0]: pos = 'first-sibling' if pos == 'first-sibling': target = siblings[0] # ok let's move this cursor = self._get_database_cursor('write') move_right = cls._move_right gap = self.rgt - self.lft + 1 sql = None target_tree = target.tree_id # first make a hole if pos == 'last-child': newpos = parent.rgt sql, params = move_right(target.tree_id, newpos, False, gap) elif target.is_root(): newpos = 1 if pos == 'last-sibling': target_tree = target.get_siblings().reverse()[0].tree_id + 1 elif pos == 'first-sibling': target_tree = 1 sql, params = cls._move_tree_right(1) elif pos == 'left': sql, params = cls._move_tree_right(target.tree_id) else: if pos == 'last-sibling': newpos = target.get_parent().rgt sql, params = move_right(target.tree_id, newpos, False, gap) elif pos == 'first-sibling': newpos = target.lft sql, params = move_right(target.tree_id, newpos - 1, False, gap) elif pos == 'left': newpos = target.lft sql, params = move_right(target.tree_id, newpos, True, gap) if sql: cursor.execute(sql, params) # we reload 'self' because lft/rgt may have changed fromobj = cls.objects.get(pk=self.pk) depthdiff = target.depth - fromobj.depth if parent: depthdiff += 1 # move the tree to the hole sql = "UPDATE %(table)s "\ " SET tree_id = %(target_tree)d, "\ " lft = lft + %(jump)d , "\ " rgt = rgt + %(jump)d , "\ " depth = depth + %(depthdiff)d "\ " WHERE tree_id = %(from_tree)d AND "\ " lft BETWEEN %(fromlft)d AND %(fromrgt)d" % { 'table': connection.ops.quote_name(cls._meta.db_table), 'from_tree': fromobj.tree_id, 'target_tree': target_tree, 'jump': newpos - fromobj.lft, 'depthdiff': depthdiff, 'fromlft': fromobj.lft, 'fromrgt': fromobj.rgt} cursor.execute(sql, []) # close the gap sql, params = cls._get_close_gap_sql(fromobj.lft, fromobj.rgt, fromobj.tree_id) cursor.execute(sql, params) transaction.commit_unless_managed()
def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. """ pos = self._fix_move_opts(pos) stmts = [] sib_order = None parent = None if pos in ('first-child', 'last-child', 'sorted-child'): # moving to a child if not target.is_leaf(): target = target.get_last_child() pos = { 'first-child': 'first-sibling', 'last-child': 'last-sibling', 'sorted-child': 'sorted-sibling' }[pos] else: parent = target if pos == 'sorted-child': pos = 'sorted-sibling' else: pos = 'first-sibling' sib_order = 1 if target.is_descendant_of(self): raise InvalidMoveToDescendant("Can't move node to a descendant.") if self == target and ( (pos == 'left') or \ (pos in ('right', 'last-sibling') and \ target == target.get_last_sibling()) or \ (pos == 'first-sibling' and \ target == target.get_first_sibling())): # special cases, not actually moving the node so no need to UPDATE return if pos == 'sorted-sibling': # easy, just change the parent if parent: self.parent = parent else: self.parent = target.parent else: if sib_order: self.sib_order = sib_order else: self.sib_order = self.__class__._move_add_sibling_aux( pos, target, stmts) if parent: self.parent = parent else: self.parent = target.parent if stmts: cursor = connection.cursor() for sql, vals in stmts: cursor.execute(sql, vals) self.save() transaction.commit_unless_managed()
def move(self, target, pos=None): """ Moves the current node and all it's descendants to a new position relative to another node. :raise PathOverflow: when the library can't make room for the node's new position """ pos = self._fix_move_opts(pos) oldpath = self.path # initialize variables and if moving to a child, updates "move to # child" to become a "move to sibling" if possible (if it can't # be done, it means that we are adding the first child) pos, target, newdepth, siblings, newpos = self._fix_move_to_child( pos, target, target.depth) if target.is_descendant_of(self): raise InvalidMoveToDescendant( _("Can't move node to a descendant.")) if oldpath == target.path and ( (pos == 'left') or \ (pos in ('right', 'last-sibling') and \ target.path == target.get_last_sibling().path) or \ (pos == 'first-sibling' and \ target.path == target.get_first_sibling().path)): # special cases, not actually moving the node so no need to UPDATE return if pos == 'sorted-sibling': siblings = self.get_sorted_pos_queryset(target.get_siblings(), self) try: newpos = self._get_lastpos_in_path(siblings.all()[0].path) except IndexError: newpos = None if newpos is None: pos = 'last-sibling' stmts = [] # generate the sql that will do the actual moving of nodes oldpath, newpath = self._move_add_sibling_aux(pos, newpos, newdepth, target, siblings, stmts, oldpath, True) # updates needed for mysql and children count in parents self._updates_after_move(oldpath, newpath, stmts) cursor = connection.cursor() for sql, vals in stmts: cursor.execute(sql, vals) transaction.commit_unless_managed() #change various fields of self in memory after it is moved table_name = self._meta.db_table sql = "Select path, depth from %s where id=%%s" % (table_name, ) cursor.execute(sql, [self.id]) row = cursor.fetchone() self.path = row[0] self.depth = row[1]