def delete_branch(cls, resource_id=None, db_session=None, *args, **kwargs): """ This deletes whole branch with children starting from resource_id :param resource_id: :param db_session: :return: """ tablename = cls.model.__table__.name # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) parent_id = resource.parent_id ordering = resource.ordering raw_q = """ WITH RECURSIVE subtree AS ( SELECT res.resource_id FROM {tablename} AS res WHERE res.resource_id = :resource_id UNION ALL SELECT res_u.resource_id FROM {tablename} res_u, subtree st WHERE res_u.parent_id = st.resource_id ) DELETE FROM resources where resource_id in (select * from subtree); """.format(tablename=tablename) db_session = get_db_session(db_session) text_obj = sa.text(raw_q) db_session.execute(text_obj, params={'resource_id': resource_id}) cls.shift_ordering_down(parent_id, ordering, db_session=db_session) return True
def set_position(cls, resource_id, to_position, db_session=None, *args, **kwargs): """ Sets node position for new node in the tree :param resource_id: resource to move :param to_position: new position :param db_session: :return:def count_children(cls, resource_id, db_session=None): """ db_session = get_db_session(db_session) # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) cls.check_node_position(resource.parent_id, to_position, on_same_branch=True, db_session=db_session) cls.shift_ordering_up(resource.parent_id, to_position, db_session=db_session) db_session.flush() db_session.expire(resource) resource.ordering = to_position return True
def check_node_parent(cls, resource_id, new_parent_id, db_session=None, *args, **kwargs): """ Checks if parent destination is valid for node :param resource_id: :param new_parent_id: :param db_session: :return: """ db_session = get_db_session(db_session) new_parent = ResourceService.lock_resource_for_update( resource_id=new_parent_id, db_session=db_session) # we are not moving to "root" so parent should be found if not new_parent and new_parent_id is not None: raise ZigguratResourceTreeMissingException( 'New parent node not found') else: result = cls.path_upper(new_parent_id, db_session=db_session) path_ids = [r.resource_id for r in result] if resource_id in path_ids: raise ZigguratResourceTreePathException( 'Trying to insert node into itself')
def check_node_parent(cls, resource_id, new_parent_id, db_session=None, *args, **kwargs): """ Checks if parent destination is valid for node :param resource_id: :param new_parent_id: :param db_session: :return: """ db_session = get_db_session(db_session) new_parent = ResourceService.lock_resource_for_update( resource_id=new_parent_id, db_session=db_session) # we are not moving to "root" so parent should be found if not new_parent and new_parent_id is not None: raise ZigguratResourceTreeMissingException( 'New parent node not found') else: result = cls.path_upper(new_parent_id, db_session=db_session) path_ids = [r.resource_id for r in result] if resource_id in path_ids: raise ZigguratResourceTreePathException( 'Trying to insert node into itself')
def delete_branch(cls, resource_id=None, db_session=None, *args, **kwargs): """ This deletes whole branch with children starting from resource_id :param resource_id: :param db_session: :return: """ tablename = cls.model.__table__.name # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) parent_id = resource.parent_id ordering = resource.ordering raw_q = """ WITH RECURSIVE subtree AS ( SELECT res.resource_id FROM {tablename} AS res WHERE res.resource_id = :resource_id UNION ALL SELECT res_u.resource_id FROM {tablename} res_u, subtree st WHERE res_u.parent_id = st.resource_id ) DELETE FROM resources where resource_id in (select * from subtree); """.format(tablename=tablename) db_session = get_db_session(db_session) text_obj = sa.text(raw_q) db_session.execute(text_obj, params={'resource_id': resource_id}) cls.shift_ordering_down(parent_id, ordering, db_session=db_session) return True
def set_position(cls, resource_id, to_position, db_session=None, *args, **kwargs): """ Sets node position for new node in the tree :param resource_id: resource to move :param to_position: new position :param db_session: :return:def count_children(cls, resource_id, db_session=None): """ db_session = get_db_session(db_session) # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) cls.check_node_position( resource.parent_id, to_position, on_same_branch=True, db_session=db_session) cls.shift_ordering_up(resource.parent_id, to_position, db_session=db_session) db_session.flush() db_session.expire(resource) resource.ordering = to_position return True
def move_to_position(cls, resource_id, to_position, new_parent_id=noop, db_session=None, *args, **kwargs): """ Moves node to new location in the tree :param resource_id: resource to move :param to_position: new position :param new_parent_id: new parent id :param db_session: :return: """ db_session = get_db_session(db_session) # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) parent = ResourceService.lock_resource_for_update( resource_id=resource.parent_id, db_session=db_session) same_branch = False # reset if parent is same as old if new_parent_id == resource.parent_id: new_parent_id = noop if new_parent_id is not noop: cls.check_node_parent(resource_id, new_parent_id, db_session=db_session) else: same_branch = True if new_parent_id is noop: # it is not guaranteed that parent exists parent_id = resource.parent_id if resource else None else: parent_id = new_parent_id cls.check_node_position(parent_id, to_position, on_same_branch=same_branch, db_session=db_session) # move on same branch if new_parent_id is noop: order_range = list(sorted((resource.ordering, to_position))) move_down = resource.ordering > to_position query = db_session.query(cls.model) query = query.filter(cls.model.parent_id == parent_id) query = query.filter(cls.model.ordering.between(*order_range)) if move_down: query.update({cls.model.ordering: cls.model.ordering + 1}, synchronize_session=False) else: query.update({cls.model.ordering: cls.model.ordering - 1}, synchronize_session=False) db_session.flush() db_session.expire(resource) resource.ordering = to_position # move between branches else: cls.shift_ordering_down(resource.parent_id, resource.ordering, db_session=db_session) cls.shift_ordering_up(new_parent_id, to_position, db_session=db_session) db_session.expire(resource) resource.parent_id = new_parent_id resource.ordering = to_position db_session.flush() return True
def move_to_position(cls, resource_id, to_position, new_parent_id=noop, db_session=None, *args, **kwargs): """ Moves node to new location in the tree :param resource_id: resource to move :param to_position: new position :param new_parent_id: new parent id :param db_session: :return: """ db_session = get_db_session(db_session) # lets lock rows to prevent bad tree states resource = ResourceService.lock_resource_for_update( resource_id=resource_id, db_session=db_session) parent = ResourceService.lock_resource_for_update( resource_id=resource.parent_id, db_session=db_session) same_branch = False # reset if parent is same as old if new_parent_id == resource.parent_id: new_parent_id = noop if new_parent_id is not noop: cls.check_node_parent(resource_id, new_parent_id, db_session=db_session) else: same_branch = True if new_parent_id is noop: # it is not guaranteed that parent exists parent_id = resource.parent_id if resource else None else: parent_id = new_parent_id cls.check_node_position( parent_id, to_position, on_same_branch=same_branch, db_session=db_session) # move on same branch if new_parent_id is noop: order_range = list(sorted((resource.ordering, to_position))) move_down = resource.ordering > to_position query = db_session.query(cls.model) query = query.filter(cls.model.parent_id == parent_id) query = query.filter(cls.model.ordering.between(*order_range)) if move_down: query.update({cls.model.ordering: cls.model.ordering + 1}, synchronize_session=False) else: query.update({cls.model.ordering: cls.model.ordering - 1}, synchronize_session=False) db_session.flush() db_session.expire(resource) resource.ordering = to_position # move between branches else: cls.shift_ordering_down(resource.parent_id, resource.ordering, db_session=db_session) cls.shift_ordering_up(new_parent_id, to_position, db_session=db_session) db_session.expire(resource) resource.parent_id = new_parent_id resource.ordering = to_position db_session.flush() return True