Home » Django & REST Api » How to implement writable nested model serializer (Json object in another JSON object) for Django REST Framework

How to implement writable nested model serializer (Json object in another JSON object) for Django REST Framework

As we mentioned in the title of this post, the purpose of this post is to demonestrate how we can Django REST Framework API which works with payload JSON which contains one object (e.g. useraddress) inside another Json object like as below,

[
    {
        "id": 1,
        "userid": "my_userid",
        "username": "lynxbee1",
        "email": "social(at)lynxbee.com",
        "age": "47",
        "useraddress": {
            "id": 1,
            "home_no": "123",
            "street": "my home street",
            "city": "my city",
            "pincode": 123456
        }
    }
]

This post is in continuation with our another post “Developing REST API using functions in DRF application views” which shows how you can use function based views to implement REST API. In this post, we will just modify same codebase,

In our post we had implemented code which used only one single JSON object like below,

[{
	"id": 1,
        "userid": "my_userid",
	"username": "lynxbee1",
	"email": "social(at) lynxbee.com",
	"age": "47"
}]

Now, in this we post, as we can see in initial JSON, we want one more JSON object “useraddress” in this outer Json object which represent single user., so lets see how this can be done

vim helloproject/helloapp/models.py
from django.db import models

class UserAddress(models.Model) :
    home_no = models.CharField(max_length=100)
    street = models.CharField(max_length=100)
    city = models.CharField(max_length=100)
    pincode = models.IntegerField()

    def __str__(self) :
        return self.home_no

class UserInfo (models.Model) :
    userid = models.CharField(max_length=100)
    username = models.CharField(max_length=100)
    email = models.CharField(max_length=100)
    age = models.CharField(max_length=100)

    useraddress = models.ForeignKey('UserAddress', on_delete=models.CASCADE)

    def __str__(self) :
        return self.username

As you can see in above code, we defined class “UserAddress” which will achieve the “useraddress” json object dependent tables and then we referenced this class from original “UserInfo” class, so the actual code address compared to previous code was as,

+class UserAddress(models.Model) :
+    home_no = models.CharField(max_length=100)
+    street = models.CharField(max_length=100)
+    city = models.CharField(max_length=100)
+    pincode = models.IntegerField()
+
+    def __str__(self) :
+        return self.home_no
+

 class UserInfo (models.Model) :
 
+    useraddress = models.ForeignKey('UserAddress', on_delete=models.CASCADE)
+
     def __str__(self) :
         return self.username

Next now, we need to change the serializers.py for our app, as below,

vim helloproject/helloapp/serializers.py
from rest_framework import serializers
from helloproject.helloapp.models import UserInfo, UserAddress

from drf_writable_nested import WritableNestedModelSerializer

class UserAddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserAddress
        fields = ['id', 'home_no', 'street', 'city', 'pincode']

class UserInfoSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
    useraddress = UserAddressSerializer()

    class Meta:
        model = UserInfo
        fields = ['id', 'userid', 'username', 'email', 'age', 'useraddress']

As you can see above, we created totally new “UserAddressSerializer” class and have referenced this “UserAddressSerializer” class as a variable “useraddress” inside UserInfoSerializer and then same variable we appended at last to fields.

Hence the actual code which we added / changed was like below,

+from helloproject.helloapp.models import UserInfo, UserAddress
+
+from drf_writable_nested import WritableNestedModelSerializer
+
+class UserAddressSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = UserAddress
+        fields = ['id', 'home_no', 'street', 'city', 'pincode']
+
+class UserInfoSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
+    useraddress = UserAddressSerializer()
 
-class UserInfoSerializer(serializers.ModelSerializer):
     class Meta:
         model = UserInfo
-        fields = ('id', 'userid', 'username', 'email', 'age')
+        fields = ['id', 'userid', 'username', 'email', 'age', 'useraddress']

Notice here, apart from just creating one more “UserAddressSerializer” and linking it with “UserInfoSerializer” we have also changed UserInfoSerializer as below, with additional argument WritableNestedModelSerializer,

-class UserInfoSerializer(serializers.ModelSerializer):
+class UserInfoSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):

The reason for this has been explained in our another post, “Solved : The .create() method does not support writable nested fields by default.”

Now start the server as,

$ vim run_server.sh
#!/bin/bash

SERVER_IP="192.168.0.106"
SERVER_PORT="8000"

python3 -m venv env
source env/bin/activate
pip install django
pip install djangorestframework
pip install drf_writable_nested
python manage.py makemigrations
python manage.py migrate --run-syncdb
python manage.py migrate
python manage.py runserver $SERVER_IP:$SERVER_PORT
$ bash run_server.sh

Once the server is running from another shell, lets try to push the nested JSON as payload,

$ vim http_post.sh
#!/bin/bash

API_URL="http://192.168.0.106:8000/users"

userid="my_userid"
username="lynxbee1"
email="social(at)lynxbee.com"
age="45"

home_no=123
street="my home street"
city="my city"
pincode=123456

address="{\"home_no\":\"$home_no\",\"street\":\"$street\",\"city\":\"$city\",\"pincode\":\"$pincode\"}"

data="{\"userid\":\"$userid\",\"username\":\"$username\",\"email\":\"$email\",\"age\":\"$age\", \"useraddress\":"$address"}"
echo $data

#exit
curl -v -k -X POST -H "\"Accept: application/json\"" -H "\"Content-Type:application/json\"" -d $data "$API_URL/"
$ bash http_post.sh

And now when you see the JSON in django dashboard at http://192.168.0.106:8000/users/ you should see the JSON with another object inside main object as required and mentioned at beginning of the post.

You can download complete source from github at https://github.com/lynxbee/drf_api_view_with_function.git

Leave a Comment