在django中,如何序列化mptt树?

pfc*_*pfc 4 python django django-mptt

以下是我的代码:

class File(MPTTModel):
    name=models.CharField(max_length=36, primary_key=True)
    parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
    num=models.IntegerField(null=True)
    class MPTTMeta:
        order_insertion_by = ['name']
Run Code Online (Sandbox Code Playgroud)

我尝试使用以下代码序列化此类:

class RecursiveField(serializers.Serializer):
    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)
        return serializer.data

class FileSerializer(serializers.ModelSerializer):
    parent=RecursiveField(many=True)
    class Meta:
        model = File
        fields=('name','num','parent')
Run Code Online (Sandbox Code Playgroud)

但我失败了,我只能输出这棵树的根节点的内容。似乎序列化器无法访问根的孩子,进一步访问孩子的孩子......具体问题是在输出中, 'parent' 显示为 'null',但实际上它有 4 个孩子,每个孩子都包含几个后代。我的代码有什么问题?谢谢大家帮助我!

spe*_*ras 6

MPTT 和 REST 框架都没有什么神奇之处。

MPTT将新字段添加到您的模块中,以实现嵌套集模型。它还跟踪从子级到父级的向上链接,用于一些优化,并在嵌套集树损坏时重建嵌套集树。

所以基本上,您的模型具有以下字段name/num您手动添加的字段,您添加parent这些字段以触​​发 MPTT,以及以下自动字段:

  • tree_id: 树标识符。连接到同一根的所有节点共享相同的tree_id.
  • ? level:树中节点的深度。
  • lft/ rght:嵌套集索引。参见上面的链接,但基本思想是一个节点是另一个节点的后代,如果它lft大于或等于另一个节点,并且它小于或等于rght另一个节点的节点。

REST 框架不是 mptt 感知的,也不需要感知。它只会看到一个带有 7 个字段的常规模型,它会很高兴地对其进行序列化。

虽然您可以实现一个递归序列化器来塑造...对象的对象的嵌套对象中的序列化表示,但此时通常不是一个好主意。

现在,如果你真的想这样做,你需要以另一种方式去做。您必须序列化根节点,并确保它们的序列化表示递归地包含它们的所有子节点。不是相反,就像你在这里尝试的那样。

这个想法是构建这样的东西:

class FileSerializer(serializers.ModelSerializer):
    children = FileSerializer(many=True)
    class Meta:
        model = File
        fields=('name','num')
Run Code Online (Sandbox Code Playgroud)

但是你不能这样做,因为FileSerializer在你想要的时候没有定义。您可以尝试覆盖构造函数并在那里插入额外的序列化程序,如下所示:

class FileSerializer(serializers.ModelSerializer):
    class Meta:
        model = File
        fields=('name','num')

    def get_fields(self):
        fields = super(FileSerializer, self).get_fields()
        fields['children'] = FileSerializer(many=True)
        return fields
Run Code Online (Sandbox Code Playgroud)

未经测试,但你明白了。

但是

  • 以这种幼稚的方式这样做是一个非常糟糕的主意,因为树中的每个非叶节点都会触发对数据库的额外查询以获取其子节点。
  • 这不适用于反序列化。

怎么样,你刚刚连载平节点,并重建在客户端对象树,如果你真的需要?

[{'id': 1, 'name': 'foo', 'parent': null},  // /foo
 {'id': 2, 'name': 'bar1', 'parent': 1},    // /foo/bar1
 {'id': 3, 'name': 'bar2', 'parent': 1},    // /foo/bar2
 {'id': 4, 'name': 'foo2', 'parent': null}, // /foo2
 {'id': 5, 'name': 'baz1', 'parent': 4},    // /foo2/baz1
 {'id': 6, 'name': 'baz2', 'parent': 4}]    // /foo2/baz2
Run Code Online (Sandbox Code Playgroud)

  • 为了完成上述答案,这里是 javascript 中“treeify”函数的链接 /sf/ask/1565739801/ (2认同)