django-mptt:如何成功移动节点

5 投票
2 回答
4622 浏览
提问于 2025-04-15 23:57

django-mptt 让我快要抓狂了。我正在尝试做一件相对简单的事情:我要删除一个节点,并且需要对这个节点的孩子们做一些合理的处理。也就是说,我想把它们提升一级,让它们成为当前父节点的父节点的孩子。

比如,如果树的结构是这样的:

 Root
  |
Grandpa
  |
Father
|    |
C1   C2

我打算删除“父亲”,希望 C1 和 C2 能成为“爷爷”的孩子。

这是我正在使用的代码:

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

    def reparent_children(self, parent):
        print "Reparenting"
        for child in self.get_children():
            print "Working on", child.first_name, "to parent", parent.email
            parent = Node.objects.get(id=parent.id)
            child.move_to(parent, 'last-child')
            child.save()

所以我会调用:

father.reparent_children(grandpa)
father.parent = None
father.save()

这个方法几乎能成功。孩子们报告它们的父亲是“爷爷”:

c1.parent == grandpa  # True

“爷爷”把 C1 和 C2 视为它的孩子。

c1 in grandpa.children.all()   # True

但是,“根节点”却不承认这些孩子。

c1.get_root() == father  # c1's root is father, instead of Root

c1 in root.get_descendants()  # False

我该怎么做才能让孩子们顺利移动,同时又不让根节点出问题呢?

2 个回答

1

这段库的使用让我这几天感到很困惑——move_to 似乎并没有按我想的那样工作,而且我的树结构总是不同步。我想出了一个我更有信心的解决方案,虽然速度上可能会慢一些,而且方法也比较不常规。

这个解决方案主要是围绕一个叫 partial_rebuild 的管理方法展开的,具体可以在这里找到。

def delete_node(self):
    if not self.parent:
        print("Should not delete root node, confusing behavior follows")
        return
    tree_id = self.tree_id
    parent = self.parent

    for child in self.get_children():
        child.parent = parent
        child.save()

    self.delete()
    Node.objects.partial_rebuild(tree_id)

如果你愿意,可以用 child.move_node(parent) 来替换 child.parent = parent。

6

内部的 lftrght 值会在你第一次保存一个子项的时候发生变化(也就是你 reparent_children 方法的最后一行)。save() 方法不会更新你可能已经存在的实例。我觉得一个安全的做法是每次都从数据库重新获取它们,像这样:

def reparent_children(self, parent):
    print "Reparenting"
    for child in self.get_children():
        print "Working on", child.first_name, "to parent", parent.email
        parent = Node.objects.get(id=parent.id)
        current_child = Node.objects.get(id = child.id)
        current_child.move_to(parent, 'last-child')
        current_child.save()

我之前也遇到过 类似的问题,这个方法解决了我的问题。

撰写回答