The follows is my code:
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']
And I try to serialize this class using the following code:
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')
But I fail in that I can only output the contents of the root node of this tree.It seems the serializer cannot access the children of the root, and further the children of the children... The specific problem is that in the output, the 'parent' is shown to be 'null', but in fact it has 4 children and each of them contains several descendants. What's wrong with my code? Thank you all for helping me!
There is no magic about neither MPTT nor REST framework.
MPTT adds new fields into your module, so as to implement a nested set model. It also tracks an upwards link from children to their parent, which it uses for some optimizations, and to rebuild the nested set tree, should it get corrupted.
So basically, your model has the following fields name
/num
that you added manually, parent
which you added to trigger MPTT, and the following automatic fields:
tree_id
: a tree identifier. All nodes attached to the same root share the same tree_id
.level
: the depth of the node within the tree.lft
/rght
: the nested set indices. See the link above, but the basic idea is a node is a descendant of another node if its lft
is greated or equal and its rght
is lower or equal to that of the other node.REST framework is not mptt-aware, and does not need to be. It will just see a regular model with 7 fields, which it will happily serialize.
While you may implement a recursive serializer to shape the serialized representation in a nested object of objects of objects of objects of..., it is usually not a good idea at this point.
Now, if you actually want to do that, you need to do it the other way. You must serialize the root nodes, and make sure their serialized representation recursively includes all of their children. Not the other way around, like you tried here.
The idea would be to construct something like this:
class FileSerializer(serializers.ModelSerializer):
children = FileSerializer(many=True)
class Meta:
model = File
fields=('name','num')
But you cannot do it this way, as FileSerializer
is not defined at the point you want it. You could try to override the constructor and insert the additional serializer there, like this:
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
Untested, but you get the idea.
However:
How about you just serialize flat nodes, and rebuild the object tree on the client side if you really need to?
[{'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