[Answered ]-Django MPTT – how expensive is objects.rebuild?

2πŸ‘

βœ…

For future reference..

objects.rebuild() is only needed in some special cases. Normally mptt sets the left and right values of your nodes correctly just by setting the parent id of your node. This is also mentioned in the docs

I experienced an issue with not correctly set left, right values because I saved the new node, !before! I reset the position values of the other, already existing siblings. When inserting new nodes for a tree with the meta attribute order_insertion_by set, one has to first reset the order_insertion_by value for all siblings, and after that, save the new node. This way mptt is able to recalculate correctly the left right values.

See my (simplified) example below:

models.py

class Node(MPTTModel):
    """
    Representation of a single node
    """
    name = models.CharField(max_length=200)
    parent = TreeForeignKey('self', null=True, blank=True, related_name='%(app_label)s_%(class)s_children')
    position = models.PositiveIntegerField(max_length=10) #for nodes on the same hierarchy level we have to define the position in which they are displayed 

    class MPTTMeta:
        order_insertion_by = ['position']

views.py

new_node = Node(name="new node", parent=parent_node, position=1)
update_node_positions(new_node, 1) #routine to update the node positions
new_node.save()

update_node_positions

def update_node_positions(node, mode):
    """
    Procedure to update the node positions
    Three different modes available:
        1 = insert node
        2 = update position, parent stays the same
        3 = update position and change parent
        4 = trashed
    """

    if mode == 1 or mode==3:
        #if node has been inserted at the beginning
        if node.position == 1:
            node.get_siblings().update(position=F('position') + 1)
        #if node has been inserted not at beginning and not at the last position
        elif node.position != node.get_siblings().count() + 1:
            #update positions of siblings right of node by one
            node.get_siblings().filter(position__gte=node.position).update(position=F('position') + 1)
        if mode == 3:
            #since we removed the node from a parent, we have to decrement the positions of the former siblings right of the node by one
            if node._original_parent is not None:
                #do updates only for nodes which had a parent before. will not be executed for root nodes
                node._original_parent.get_children().filter(position__gt=node._original_position).update(position=F('position') - 1)
    if mode == 2:
        #if old position is left of new position -> decrement position by 1 for nodes which have position <= node.position AND > node.original_position
        if node.position > node._original_position:
            node.get_siblings().filter(Q(position__lte=node.position) & Q(position__gt=node._original_position)).update(position=F('position') - 1)
        #if old position is right of new position -> increment position by 1 for nodes which have position >= node.position AND < node.original_position 
        if node.position < node._original_position:
            node.get_siblings().filter(Q(position__gte=node.position) & Q(position__lt=node._original_position)).update(position=F('position') + 1)
    if mode == 4:
        #decrement position by 1 for nodes which have position > node.position
        node.get_siblings().filter(Q(position__gt=node.position)).update(position=F('position') - 1)

Leave a comment