views:

324

answers:

1

I'm attempting to use django-mptt with very little luck. This is with Python2.5, windows, sqlite3, Django 1.2pre , django-mptt latest from svn.

The code:

model:

class Node(models.Model):
    name   = models.CharField(max_length=20, blank=True)
    parent = models.ForeignKey('self', null=True, blank=True, related_name='children')

    def __unicode__(self):
        return self.name

mptt.register(Node)

setup:

nodes = []
for i in range(15):
    n = Node(name='node'+str(i))
    n.save()
    nodes.append(n)

nodes[0].move_to(None)
nodes[0].save()
for n in range(1,15):
    nodes[n].move_to(nodes[(n-1)/2],'last-child')
    nodes[n].save()

This should create a tree with one root and two children hanging off of each non-leaf node.

Now the fun begins:

>>> nodes[0].children.all()
[<Node: node1>, <Node: node2>]
>>> nodes[0].get_descendants()
[]

>>> nodes[0].get_descendants(True)
[<Node: node0>, <Node: node2>]


>>> for n in nodes:
...     print n, n.get_ancestors()
...
node0 []
node1 [<Node: node0>]
node2 [<Node: node0>]
node3 [<Node: node0>, <Node: node2>]
node4 [<Node: node0>, <Node: node2>]
node5 [<Node: node0>, <Node: node2>]
node6 [<Node: node0>, <Node: node2>]
node7 [<Node: node0>, <Node: node2>, <Node: node6>]
node8 [<Node: node0>, <Node: node2>, <Node: node6>]
node9 [<Node: node0>, <Node: node2>, <Node: node6>]
node10 [<Node: node0>, <Node: node2>, <Node: node6>]
node11 [<Node: node0>, <Node: node2>, <Node: node6>]
node12 [<Node: node0>, <Node: node2>, <Node: node6>]
node13 [<Node: node0>, <Node: node2>, <Node: node6>]
node14 [<Node: node0>, <Node: node2>, <Node: node6>]

Why are so many of the ancestors wrong? For example, node 10 should have ancestors, (0,1,10)

Am I doing something wrong or are there bugs in django-mptt?

+4  A: 

I wouldn't say it's buggy, but there is a gotcha that you need to be aware of.

When you add a child to a parent, the child's tree attributes are correctly updated with the MPTT-specific lft, rght and level values.

However, django-mptt does not update the version of the parent that you are holding. The version in the database is updated, but the copy in your local variable is not (remember that Django model instances don't have identity, so don't get updated when the database, or other instances referring to the same database row, update).

This means that the next child you add to the parent object will get the wrong left and right values, and if you do subsequently save the parent that too will have the wrong values.

The solution is to reload the parent from the database each time you add a child:

for n in range(1,15):
    parent_pos = (n-1)/2
    parent = nodes[parent_pos]
    nodes[n].move_to(parent, 'last-child')
    nodes[n].save()
    nodes[parent_pos] = Node.objects.get(pk=parent.pk)
Daniel Roseman