Permalink
Browse files

Added a pre_move signal to the Topic.merge_to() method.

Applications using topic hierarchies can then set up things like URL
rewrites based on subtrees that have moved. The signal is emitted
*before* any moves take place so that the original names of the topics
can still be determined.
  • Loading branch information...
1 parent 6ce0cba commit c3d9caf67fdf4027dc8cf6890672156510ac379e @malcolmt committed Aug 29, 2010
Showing with 35 additions and 10 deletions.
  1. +23 −8 acacia/models.py
  2. +4 −0 acacia/signals.py
  3. +8 −2 acacia/tests/test_models.py
View
@@ -53,15 +53,25 @@ def full_name(self):
def merge_to(self, parent):
"""
A variant on mptt's move_to() method that merges any overlapping
- portions of the move. If any nodes are to be merged, a pre_merge signal
- is emitted before the move, with a list (old_id, new_id) pairs for
- nodes that will be merged rather than moved. (For efficiency reasons,
- no signal is emitted if no nodes are to be merged.)
+ portions of the move.
+
+ If any nodes are to be merged, a pre_merge signal is emitted before the
+ move, with a list (old_id, new_id) pairs for nodes that will be merged
+ rather than moved. (For efficiency reasons, no signal is emitted if no
+ nodes are to be merged.)
+
+ A pre_move signal is also emitted containing the root nodes of any
+ subtrees that are being moved, but not merged, and their target parent
+ node. This enables dependent apps to set up things like URL rewrites if
+ they have URLs depending on topic names. (The significance of
+ moved-but-not-merged is that these nodes won't have their pk values
+ changed in the process.)
"""
manager = self.__class__.objects
try:
merge_node = manager.get(parent=parent, name=self.name)
except self.DoesNotExist:
+ signals.pre_move.send(sender=self, moving=[(self, parent)])
self.move_to(parent)
return
squash = [(self.id, merge_node.id)]
@@ -79,12 +89,17 @@ def merge_to(self, parent):
squash.append((child.id, conflicts[child.name].id))
examine.append((child, conflicts[child.name]))
else:
- to_move.append((child, merge_node.id))
+ to_move.append((child, merge_node))
if squash:
signals.pre_merge.send(sender=self, merge_pairs=squash)
- for child, target_parent_id in to_move:
- target_parent = manager.get(id=target_parent_id)
- child.move_to(target_parent)
+
+ if to_move:
+ signals.pre_move.send(sender=self, moving=to_move)
+ for child, new_parent in to_move:
+ # Need to refresh the instance, as previous moves might have
+ # changed the "lft" and "rght" attributes.
+ new_parent = manager.get(id=new_parent.id)
+ child.move_to(new_parent)
self.delete()
class Topic(AbstractTopic):
View
@@ -1,4 +1,8 @@
from django import dispatch
+# FIXME: Document!
pre_merge = dispatch.Signal(providing_args=["merge_pairs"])
+# FIXME: Document!
+pre_move = dispatch.Signal(providing_args=["moving"])
+
@@ -197,9 +197,14 @@ def test_merge_without_overlap(self):
target = models.Topic.objects.get_by_full_name("a")
node = models.Topic.objects.get_by_full_name("c/b/d")
signals.pre_merge.connect(self.signal_catcher)
+ signals.pre_move.connect(self.signal_catcher)
node.merge_to(target)
signals.pre_merge.disconnect(self.signal_catcher)
- self.failIf(self.signals, self.signals)
+ self.failUnlessEqual(len(self.signals), 1)
+ signal_dict = self.signals[0][1]
+ self.failUnlessEqual(signal_dict["signal"], signals.pre_move)
+ self.failUnlessEqual(signal_dict["moving"][0][0].id, node.id)
+ self.failUnlessEqual(signal_dict["moving"][0][1].id, target.id)
try:
models.Topic.objects.get_by_full_name("a/d")
except models.Topic.DoesNotExist:
@@ -212,7 +217,8 @@ def test_merge_without_overlap(self):
def test_merge_moves_all_subtrees(self):
"""
Tests that if a merge_to() call results in some merges at the top of
- the subtree, *all* unmerged children are merged.
+ the subtree, *all* unmerged children are moved correctly (descendants
+ that aren't direct children of a merged node are the problematic cases).
"""
z_child = models.Topic.objects.get_or_create_by_full_name("x/c/z")[0]
parent = models.Topic.objects.get_by_full_name("a")

0 comments on commit c3d9caf

Please sign in to comment.