Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest framework nested self-referential objects

I have model that looks like this:

class Category(models.Model):     parentCategory = models.ForeignKey('self', blank=True, null=True, related_name='subcategories')     name = models.CharField(max_length=200)     description = models.CharField(max_length=500) 

I managed to get flat json representation of all categories with serializer:

class CategorySerializer(serializers.HyperlinkedModelSerializer):     parentCategory = serializers.PrimaryKeyRelatedField()     subcategories = serializers.ManyRelatedField()      class Meta:         model = Category         fields = ('parentCategory', 'name', 'description', 'subcategories') 

Now what I want to do is for subcategories list to have inline json representation of subcategories instead of their ids. How would I do that with django-rest-framework? I tried to find it in documentation, but it seems incomplete.

like image 246
Jacek Chmielewski Avatar asked Nov 14 '12 10:11

Jacek Chmielewski


2 Answers

Instead of using ManyRelatedField, use a nested serializer as your field:

class SubCategorySerializer(serializers.ModelSerializer):     class Meta:         model = Category         fields = ('name', 'description')  class CategorySerializer(serializers.ModelSerializer):     parentCategory = serializers.PrimaryKeyRelatedField()     subcategories = serializers.SubCategorySerializer()      class Meta:         model = Category         fields = ('parentCategory', 'name', 'description', 'subcategories') 

If you want to deal with arbitrarily nested fields you should take a look at the customising the default fields part of the docs. You can't currently directly declare a serializer as a field on itself, but you can use these methods to override what fields are used by default.

class CategorySerializer(serializers.ModelSerializer):     parentCategory = serializers.PrimaryKeyRelatedField()      class Meta:         model = Category         fields = ('parentCategory', 'name', 'description', 'subcategories')          def get_related_field(self, model_field):             # Handles initializing the `subcategories` field             return CategorySerializer() 

Actually, as you've noted the above isn't quite right. This is a bit of a hack, but you might try adding the field in after the serializer is already declared.

class CategorySerializer(serializers.ModelSerializer):     parentCategory = serializers.PrimaryKeyRelatedField()      class Meta:         model = Category         fields = ('parentCategory', 'name', 'description', 'subcategories')  CategorySerializer.base_fields['subcategories'] = CategorySerializer() 

A mechanism of declaring recursive relationships is something that needs to be added.


Edit: Note that there is now a third-party package available that specifically deals with this kind of use-case. See djangorestframework-recursive.

like image 140
Tom Christie Avatar answered Sep 21 '22 23:09

Tom Christie


@wjin's solution was working great for me until I upgraded to Django REST framework 3.0.0, which deprecates to_native. Here's my DRF 3.0 solution, which is a slight modification.

Say you have a model with a self-referential field, for example threaded comments in a property called "replies". You have a tree representation of this comment thread, and you want to serialize the tree

First, define your reusable RecursiveField class

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

Then, for your serializer, use the the RecursiveField to serialize the value of "replies"

class CommentSerializer(serializers.Serializer):     replies = RecursiveField(many=True)      class Meta:         model = Comment         fields = ('replies, ....) 

Easy peasy, and you only need 4 lines of code for a re-usable solution.

NOTE: If your data structure is more complicated than a tree, like say a directed acyclic graph (FANCY!) then you could try @wjin's package -- see his solution. But I haven't had any problems with this solution for MPTTModel based trees.

like image 21
Mark Chackerian Avatar answered Sep 21 '22 23:09

Mark Chackerian