Example #1
0
 def save(self, *args, **kwargs):
     """
     If this is a new node, sets tree fields up before it is inserted
     into the database, making room in the tree structure as neccessary,
     defaulting to making the new node the last child of its parent.
 
     It the node's left and right edge indicators already been set, we
     take this as indication that the node has already been set up for
     insertion, so its tree fields are left untouched.
 
     If this is an existing node and its parent has been changed,
     performs reparenting in the tree structure, defaulting to making the
     node the last child of its new parent.
 
     In either case, if the node's class has its ``order_insertion_by``
     tree option set, the node will be inserted or moved to the
     appropriate position to maintain ordering by the specified field.
     """
     opts = self._mptt_meta
     parent_id = opts.get_raw_field_value(self, opts.parent_attr)
     
     # determine whether this instance is already in the db
     force_update = kwargs.get('force_update', False)
     force_insert = kwargs.get('force_insert', False)
     using = kwargs.get('using', None)
     manager = self.__class__._base_manager
     if hasattr(manager, 'using'):
         # multi db support was added in django 1.2
         manager = manager.using(using)
     
     if self.pk and (force_update or getattr(self, '_mptt_saved', False) or \
                 (not force_insert and _exists(manager.filter(pk=self.pk)))):
         # it already exists, so do a move
         old_parent_id = self._mptt_cached_fields[opts.parent_attr]
         same_order = old_parent_id == parent_id
         if same_order and len(self._mptt_cached_fields) > 1:
             for field_name, old_value in self._mptt_cached_fields.items():
                 if old_value != opts.get_raw_field_value(self, field_name):
                     same_order = False
                     break
         
         if not same_order:
             opts.set_raw_field_value(self, opts.parent_attr, old_parent_id)
             try:
                 right_sibling = None
                 if opts.order_insertion_by:
                     right_sibling = opts.get_ordered_insertion_target(self, getattr(self, opts.parent_attr))
                 
                 if right_sibling:
                     self.move_to(right_sibling, 'left')
                 else:
                     # Default movement
                     if parent_id is None:
                         root_nodes = self._tree_manager.root_nodes()
                         try:
                             rightmost_sibling = root_nodes.exclude(pk=self.pk).order_by('-%s' % opts.tree_id_attr)[0]
                             self.move_to(rightmost_sibling, position='right')
                         except IndexError:
                             pass
                     else:
                         parent = getattr(self, opts.parent_attr)
                         self.move_to(parent, position='last-child')
             finally:
                 # Make sure the new parent is always
                 # restored on the way out in case of errors.
                 opts.set_raw_field_value(self, opts.parent_attr, parent_id)
     else:
         # new node, do an insert
         if (getattr(self, opts.left_attr) and getattr(self, opts.right_attr)):
             # This node has already been set up for insertion.
             pass
         else:
             parent = getattr(self, opts.parent_attr)
             
             right_sibling = None
             if opts.order_insertion_by:
                 right_sibling = opts.get_ordered_insertion_target(self, parent)
     
             if right_sibling:
                 self.insert_at(right_sibling, 'left', allow_existing_pk=True)
                 
                 if parent:
                     # since we didn't insert into parent, we have to update parent.rght
                     # here instead of in TreeManager.insert_node()
                     right_shift = 2 * (self.get_descendant_count() + 1)
                     self._tree_manager._post_insert_update_cached_parent_right(parent, right_shift)
             else:
                 # Default insertion
                 self.insert_at(parent, position='last-child', allow_existing_pk=True)
     super(MPTTModel, self).save(*args, **kwargs)
     self._mptt_saved = True
     opts.update_mptt_cached_fields(self)
Example #2
0
    def insert_node(self, node, target, position='last-child', save=False, allow_existing_pk=False):
        """
        Sets up the tree state for ``node`` (which has not yet been
        inserted into in the database) so it will be positioned relative
        to a given ``target`` node as specified by ``position`` (when
        appropriate) it is inserted, with any neccessary space already
        having been made for it.

        A ``target`` of ``None`` indicates that ``node`` should be
        the last root node.

        If ``save`` is ``True``, ``node``'s ``save()`` method will be
        called before it is returned.
        """
        
        if self._base_manager:
            return self._base_manager.insert_node(node, target, position=position, save=save)
        
        if node.pk and not allow_existing_pk and _exists(self.filter(pk=node.pk)):
            raise ValueError(_('Cannot insert a node which has already been saved.'))

        if target is None:
            setattr(node, self.left_attr, 1)
            setattr(node, self.right_attr, 2)
            setattr(node, self.level_attr, 0)
            setattr(node, self.tree_id_attr, self._get_next_tree_id())
            setattr(node, self.parent_attr, None)
        elif target.is_root_node() and position in ['left', 'right']:
            target_tree_id = getattr(target, self.tree_id_attr)
            if position == 'left':
                tree_id = target_tree_id
                space_target = target_tree_id - 1
            else:
                tree_id = target_tree_id + 1
                space_target = target_tree_id

            self._create_tree_space(space_target)

            setattr(node, self.left_attr, 1)
            setattr(node, self.right_attr, 2)
            setattr(node, self.level_attr, 0)
            setattr(node, self.tree_id_attr, tree_id)
            setattr(node, self.parent_attr, None)
        else:
            setattr(node, self.left_attr, 0)
            setattr(node, self.level_attr, 0)

            space_target, level, left, parent, right_shift = \
                self._calculate_inter_tree_move_values(node, target, position)
            tree_id = getattr(parent, self.tree_id_attr)

            self._create_space(2, space_target, tree_id)

            setattr(node, self.left_attr, -left)
            setattr(node, self.right_attr, -left + 1)
            setattr(node, self.level_attr, -level)
            setattr(node, self.tree_id_attr, tree_id)
            setattr(node, self.parent_attr, parent)
    
            if parent:
                self._post_insert_update_cached_parent_right(parent, right_shift)

        if save:
            node.save()
        return node
Example #3
0
    def insert_node(self,
                    node,
                    target,
                    position='last-child',
                    save=False,
                    allow_existing_pk=False):
        """
        Sets up the tree state for ``node`` (which has not yet been
        inserted into in the database) so it will be positioned relative
        to a given ``target`` node as specified by ``position`` (when
        appropriate) it is inserted, with any neccessary space already
        having been made for it.

        A ``target`` of ``None`` indicates that ``node`` should be
        the last root node.

        If ``save`` is ``True``, ``node``'s ``save()`` method will be
        called before it is returned.
        """

        if self._base_manager:
            return self._base_manager.insert_node(node,
                                                  target,
                                                  position=position,
                                                  save=save)

        if node.pk and not allow_existing_pk and _exists(
                self.filter(pk=node.pk)):
            raise ValueError(
                _('Cannot insert a node which has already been saved.'))

        if target is None:
            setattr(node, self.left_attr, 1)
            setattr(node, self.right_attr, 2)
            setattr(node, self.level_attr, 0)
            setattr(node, self.tree_id_attr, self._get_next_tree_id())
            setattr(node, self.parent_attr, None)
        elif target.is_root_node() and position in ['left', 'right']:
            target_tree_id = getattr(target, self.tree_id_attr)
            if position == 'left':
                tree_id = target_tree_id
                space_target = target_tree_id - 1
            else:
                tree_id = target_tree_id + 1
                space_target = target_tree_id

            self._create_tree_space(space_target)

            setattr(node, self.left_attr, 1)
            setattr(node, self.right_attr, 2)
            setattr(node, self.level_attr, 0)
            setattr(node, self.tree_id_attr, tree_id)
            setattr(node, self.parent_attr, None)
        else:
            setattr(node, self.left_attr, 0)
            setattr(node, self.level_attr, 0)

            space_target, level, left, parent, right_shift = \
                self._calculate_inter_tree_move_values(node, target, position)
            tree_id = getattr(parent, self.tree_id_attr)

            self._create_space(2, space_target, tree_id)

            setattr(node, self.left_attr, -left)
            setattr(node, self.right_attr, -left + 1)
            setattr(node, self.level_attr, -level)
            setattr(node, self.tree_id_attr, tree_id)
            setattr(node, self.parent_attr, parent)

            if parent:
                self._post_insert_update_cached_parent_right(
                    parent, right_shift)

        if save:
            node.save()
        return node
Example #4
0
    def save(self, *args, **kwargs):
        """
        If this is a new node, sets tree fields up before it is inserted
        into the database, making room in the tree structure as neccessary,
        defaulting to making the new node the last child of its parent.
    
        It the node's left and right edge indicators already been set, we
        take this as indication that the node has already been set up for
        insertion, so its tree fields are left untouched.
    
        If this is an existing node and its parent has been changed,
        performs reparenting in the tree structure, defaulting to making the
        node the last child of its new parent.
    
        In either case, if the node's class has its ``order_insertion_by``
        tree option set, the node will be inserted or moved to the
        appropriate position to maintain ordering by the specified field.
        """
        opts = self._mptt_meta
        parent_id = opts.get_raw_field_value(self, opts.parent_attr)

        # determine whether this instance is already in the db
        force_update = kwargs.get('force_update', False)
        force_insert = kwargs.get('force_insert', False)
        using = kwargs.get('using', None)
        manager = self.__class__._base_manager
        if hasattr(manager, 'using'):
            # multi db support was added in django 1.2
            manager = manager.using(using)

        if self.pk and (force_update or getattr(self, '_mptt_saved', False) or \
                    (not force_insert and _exists(manager.filter(pk=self.pk)))):
            # it already exists, so do a move
            old_parent_id = self._mptt_cached_fields[opts.parent_attr]
            same_order = old_parent_id == parent_id
            if same_order and len(self._mptt_cached_fields) > 1:
                for field_name, old_value in self._mptt_cached_fields.items():
                    if old_value != opts.get_raw_field_value(self, field_name):
                        same_order = False
                        break

            if not same_order:
                opts.set_raw_field_value(self, opts.parent_attr, old_parent_id)
                try:
                    right_sibling = None
                    if opts.order_insertion_by:
                        right_sibling = opts.get_ordered_insertion_target(
                            self, getattr(self, opts.parent_attr))

                    if right_sibling:
                        self.move_to(right_sibling, 'left')
                    else:
                        # Default movement
                        if parent_id is None:
                            root_nodes = self._tree_manager.root_nodes()
                            try:
                                rightmost_sibling = root_nodes.exclude(
                                    pk=self.pk).order_by('-%s' %
                                                         opts.tree_id_attr)[0]
                                self.move_to(rightmost_sibling,
                                             position='right')
                            except IndexError:
                                pass
                        else:
                            parent = getattr(self, opts.parent_attr)
                            self.move_to(parent, position='last-child')
                finally:
                    # Make sure the new parent is always
                    # restored on the way out in case of errors.
                    opts.set_raw_field_value(self, opts.parent_attr, parent_id)
        else:
            # new node, do an insert
            if (getattr(self, opts.left_attr)
                    and getattr(self, opts.right_attr)):
                # This node has already been set up for insertion.
                pass
            else:
                parent = getattr(self, opts.parent_attr)

                right_sibling = None
                if opts.order_insertion_by:
                    right_sibling = opts.get_ordered_insertion_target(
                        self, parent)

                if right_sibling:
                    self.insert_at(right_sibling,
                                   'left',
                                   allow_existing_pk=True)

                    if parent:
                        # since we didn't insert into parent, we have to update parent.rght
                        # here instead of in TreeManager.insert_node()
                        right_shift = 2 * (self.get_descendant_count() + 1)
                        self._tree_manager._post_insert_update_cached_parent_right(
                            parent, right_shift)
                else:
                    # Default insertion
                    self.insert_at(parent,
                                   position='last-child',
                                   allow_existing_pk=True)
        super(MPTTModel, self).save(*args, **kwargs)
        self._mptt_saved = True
        opts.update_mptt_cached_fields(self)