Usually, my goal with a JSON response is to not nest (too many) things. I prefer small, versatile, and reusable endpoints over bulky, complicated and overly nested ones. However, sometimes one might want to "read" a nested object and "write" a primary key while still using a single serializer and Django REST Framework's amazing ViewSets.
The easiest way I could think of, is to declare a nested serializer for the related object on the root serializer, as one normally would. This will return the nested data. To realize "write" we need to "undo" the nested serializer by telling the root serializer to use serializers.PrimaryKeyRelatedField
instead, which is the default field for foreign keys. This can easily be achieved by overwriting the to_internal_value
method on the root serializer.
Let's say we have two models: Category
and BlogPost
.
class Category(models.Model):
name = models.CharField(…)
description = models.TextField()
class BlogPost(models.Model):
title = models.CharField(…)
content = models.TextField()
category = models.ForeignKey(Category, on_delete=models.Cascade)
In this example, we want the BlogPost's ModelSerializer BlogPostModelSerializer
to return information about the nested category, while still allowing to link to an existing category to a blog post by using the category's id. This can easily be achieved by the following code:
class CategoryModelSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name', 'description')
class BlogPostModelSerializer(serializers.ModelSerializer):
category = CategoryModelSerializer()
class Meta:
model = BlogPost
fields = ('title', 'content', 'category')
def to_internal_value(self, data):
self.fields['category'] = serializers.PrimaryKeyRelatedField(
queryset=Category.objects.all()
)
return super().to_internal_value(data)