Skip to content

Commit

Permalink
Merge branch 'gc78-update-parents-when-inserting'
Browse files Browse the repository at this point in the history
  • Loading branch information
craigds committed Jan 22, 2011
2 parents d38bd7d + 666ddd9 commit 3ac9683
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 19 deletions.
25 changes: 21 additions & 4 deletions mptt/managers.py
Expand Up @@ -224,7 +224,7 @@ def insert_node(self, node, target, position='last-child', save=False, allow_exi
setattr(node, self.left_attr, 0)
setattr(node, self.level_attr, 0)

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

Expand All @@ -235,6 +235,9 @@ def insert_node(self, node, target, position='last-child', save=False, allow_exi
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()
Expand Down Expand Up @@ -313,6 +316,14 @@ def rebuild(self):
idx += 1
self._rebuild_helper(pk, 1, idx)
transaction.commit_unless_managed()

def _post_insert_update_cached_parent_right(self, instance, right_shift):
setattr(instance, self.right_attr, getattr(instance, self.right_attr) + right_shift)
attr = '_%s_cache' % self.parent_attr
if hasattr(instance, attr):
parent = getattr(instance, attr)
if parent:
self._post_insert_update_cached_parent_right(parent, right_shift)

def _rebuild_helper(self, pk, left, tree_id, level=0):
opts = self.model._mptt_meta
Expand Down Expand Up @@ -365,7 +376,12 @@ def _calculate_inter_tree_move_values(self, node, target, position):
raise ValueError(_('An invalid position was given: %s.') % position)

left_right_change = left - space_target - 1
return space_target, level_change, left_right_change, parent

right_shift = 0
if parent:
right_shift = 2 * (node.get_descendant_count() + 1)

return space_target, level_change, left_right_change, parent, right_shift

def _close_gap(self, size, target, tree_id):
"""
Expand Down Expand Up @@ -639,7 +655,7 @@ def _move_child_to_new_tree(self, node, target, position):
level = getattr(node, self.level_attr)
new_tree_id = getattr(target, self.tree_id_attr)

space_target, level_change, left_right_change, parent = \
space_target, level_change, left_right_change, parent, new_parent_right = \
self._calculate_inter_tree_move_values(node, target, position)

tree_width = right - left + 1
Expand All @@ -657,6 +673,7 @@ def _move_child_to_new_tree(self, node, target, position):
setattr(node, self.level_attr, level - level_change)
setattr(node, self.tree_id_attr, new_tree_id)
setattr(node, self.parent_attr, parent)

node._mptt_cached_fields[self.parent_attr] = parent.pk

def _move_child_within_tree(self, node, target, position):
Expand Down Expand Up @@ -804,7 +821,7 @@ def _move_root_node(self, node, target, position):
elif tree_id == new_tree_id:
raise InvalidMove(_('A node may not be made a child of any of its descendants.'))

space_target, level_change, left_right_change, parent = \
space_target, level_change, left_right_change, parent, right_shift = \
self._calculate_inter_tree_move_values(node, target, position)

# Create space for the tree which will be inserted
Expand Down
13 changes: 11 additions & 2 deletions mptt/models.py
Expand Up @@ -343,8 +343,11 @@ def get_descendant_count(self):
"""
Returns the number of descendants this model instance has.
"""
return (self._mpttfield('right') -
self._mpttfield('left') - 1) / 2
if self._mpttfield('right') is None:
# node not saved yet
return 0
else:
return (self._mpttfield('right') - self._mpttfield('left') - 1) / 2

def get_leafnodes(self, include_self=False):
"""
Expand Down Expand Up @@ -583,6 +586,12 @@ def save(self, *args, **kwargs):

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)
Expand Down
34 changes: 21 additions & 13 deletions mptt/tests/doctests.txt
Expand Up @@ -1307,8 +1307,8 @@ ValueError: Cannot insert a node which has already been saved.
>>> a.save()
>>> print_tree_details(OrderedInsertion.tree.all())
1 - 1 0 1 6
3 1 1 1 2 3
2 1 1 1 4 5
2 1 1 1 2 3
3 1 1 1 4 5


# reordering after save, when only a single instance at level exists ##########
Expand Down Expand Up @@ -1400,6 +1400,14 @@ ValueError: Cannot insert a node which has already been saved.
2 1 1 1 2 5
3 2 1 2 3 4

>>> OrderedInsertion.objects.all().delete()
>>> root = OrderedInsertion.objects.create(name='root')
>>> r1 = OrderedInsertion.objects.create(name='r1', parent=root)
>>> r2 = OrderedInsertion.objects.create(name='r2', parent=root)
>>> print_tree_details(OrderedInsertion.tree.all())
1 - 1 0 1 6
2 1 1 1 2 3
3 1 1 1 4 5

# Insertion of positioned nodes, multiple ordering criteria ###################
>>> r1 = MultiOrder.objects.create(name='fff', size=20, date=date(2008, 1, 1))
Expand Down Expand Up @@ -1486,31 +1494,31 @@ ValueError: Cannot insert a node which has already been saved.
>>> julie = Student.objects.create(name='Julie', parent=jess)
>>> print_tree_details(Person.my_tree_manager.all())
1 - 1 0 1 6
3 1 1 1 2 3
2 1 1 1 4 5
2 1 1 1 2 3
3 1 1 1 4 5
4 - 2 0 1 10
8 4 2 1 2 3
5 4 2 1 4 9
6 5 2 2 5 8
7 6 2 3 6 7
5 4 2 1 2 7
6 5 2 2 3 6
7 6 2 3 4 5
8 4 2 1 8 9

>>> print_tree_details(Student.tree.all())
2 1 1 1 4 5
2 1 1 1 2 3
4 - 2 0 1 10
8 4 2 1 2 3
6 5 2 2 5 8
6 5 2 2 3 6
8 4 2 1 8 9

>>> jack = Person.objects.get(pk=1)
>>> jack.get_descendants()
[<Person: Jim>, <Person: Jill>]
[<Person: Jill>, <Person: Jim>]
>>> jack.get_root()
<Person: Jack>
>>> jill = Person.objects.get(pk=3)
>>> jill.get_root()
<Person: Jack>
>>> jess = Student.objects.get(pk=4)
>>> jess.get_descendants()
[<Person: Julie>, <Person: Jeff>, <Person: Jane>, <Person: Joe>]
[<Person: Jeff>, <Person: Jane>, <Person: Joe>, <Person: Julie>]
>>> jess.get_root()
<Person: Jess>
>>> jeff = Person.objects.get(pk=5)
Expand Down

0 comments on commit 3ac9683

Please sign in to comment.