djangodjango-rest-framework

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.


Solution

  • 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.