def _is_saved(self, using=None): if not self.pk or self._mpttfield('tree_id') is None: return False opts = self._meta if opts.pk.rel is None: return True else: if not hasattr(self, '_mptt_saved'): manager = self.__class__._base_manager # NOTE we don't support django 1.1 anymore, so this is likely to get removed soon if hasattr(manager, 'using'): # multi db support was added in django 1.2 manager = manager.using(using) self._mptt_saved = _exists(manager.filter(pk=self.pk)) return self._mptt_saved
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. NOTE: This is a low-level method; it does NOT respect ``MPTTMeta.order_insertion_by``. In most cases you should just set the node's parent and let mptt call this during save. """ 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
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
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)
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)