8

I'm using Ben Firshman's fork of django-MPTT (hat tip to Daniel Roseman for the recommendation).

I've got stuck trying to re-order nodes which share a common parent. I've got a list of primary keys, like this:

ids = [5, 9, 7, 3]

All of these nodes have a parent, say with primary key 1.

At present, these nodes are ordered [5, 3, 9, 7], how can I re-order them to [5, 9, 7, 3]?

I've tried something like this:

last_m = MyModel.get(pk = ids.pop(0))
last_m.move_to(last_m.parent, position='first-child')

for id in ids:
  m = MyModel.get(pk = id)
  m.move_to(last_m, position='right')

Which I'd expect to do what I want, per the docs on move_to, but it doesn't seem to change anything. Sometimes it seems to move the first item in ids to be the first child of its parent, sometimes it doesn't.

Am I right in my reading of the docs for move_to that calling move_to on a node n with position=right and a target which is a sibling of n will move n to immediately after the target?

It's possible I've screwed up my models table in trying to figure this out, so maybe the code above is actually right. It's also possible there's a much more elegant way of doing this (perhaps one that doesn't involve O(n) selects and O(n) updates).

Have I misunderstood something?

Bonus question: is there a way of forcing django-MPTT to rebuild lft and rght values for all instances of a given model?

Community
  • 1
  • 1
Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212

1 Answers1

7

I think this is an artefact of a failure in MPTT that I've mentioned before - when you move nodes around, it correctly updates the instance of the node you're moving, but it doesn't update the instance of the target (although it does get updated in the database).

The consequence of this is that in your code, each m gets moved to the right of last_m - but the values in the last_m still reflect the position before the move, so the next move uses the original lft/right values instead of the new post-move ones.

The solution is to reload last_m each time:

for id in ids:
  last_m = MyModel.objects.get(pk=last_m.id)
  m = MyModel.get(pk = id)
  m.move_to(last_m, position='right')
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • I suspected it was something to do with that - having read a few of your responses to MPTT questions. I'll give that a go. Do you have any idea about the bonus question? – Dominic Rodger Feb 24 '10 at 14:43
  • @Daniel - forgot to close the loop on that one. Your answer worked great. Still a bit nervous about MPTT and the possibility of the tree getting into a bad state - do you have any tips for managing that? – Dominic Rodger Mar 02 '10 at 09:44
  • 6
    Old question, but for anyone that stumbles here - you can rebuild an MPTT Tree with ModelName.tree.rebuild(). This is useful if you have defined the MPTTMeta order_insertion_by field and need to update the order of your tree. – Nagyman May 03 '12 at 17:42
  • If the desired node is root, move_to function will not change the order and will only convert that node to root. Do you have a solution to change the order of the root nodes? – Ebrahim Abdollahian Jun 28 '22 at 08:46
  • To move the root nodes, you can use the move_to with left and right parameters. – Ebrahim Abdollahian Jun 28 '22 at 11:11