Create and update Django rest framework nested serializers

(Comments)

In this article we will see about the Django rest framework nested serializers and how to create a DRF writable nested serializers. This tutorial is divided into two parts. In the first part we discuss about creating models, serializers and views. In the second part we discuss how to use nested serializers and how to create and update nested serializers.

PART 1:

Copy the below code to the models.py file of you Django app.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models

# Create your models here.


class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

    def __unicode__(self):
        return self.first_name


class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE, related_name='album_musician', null=True, blank=True)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

Here we have two models 'Musician' and 'Album' and the model 'Album' has ForeignKey to model 'Musician'.

The serializers for the above two models can be written as in the code snippet below. Create a file called serializers.py and copy the below code into it.

Before that to use the djangorestframework, make sure that you have it installed. You can install it by simply running the following command.

pip install djangorestframework

After that add 'rest_framework' to your installed apps in the settings.py file.

from .models import *
from rest_framework import serializers, fields


class AlbumSerializer(serializers.ModelSerializer):

    class Meta:
        model = Album
        fields = ('id', 'artist', 'name', 'release_date', 'num_stars')


class MusicianSerializer(serializers.ModelSerializer):

    class Meta:
        model = Musician
        fields = ('id', 'first_name', 'last_name', 'instrument')

Now in the views.py file, copy the below code and save it.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.shortcuts import render
from .models import *
from .serializers import *
from rest_framework import generics
# Create your views here.


class MusicianListView(generics.ListCreateAPIView):
    queryset = Musician.objects.all()
    serializer_class = MusicianSerializer


class MusicianView(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = MusicianSerializer
    queryset = Musician.objects.all()


class AlbumListView(generics.ListCreateAPIView):
    queryset = Album.objects.all()
    serializer_class = AlbumSerializer


class AlbumView(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = AlbumSerializer
    queryset = Album.objects.all()

Once the views are ready, add urls to the urls.py file in your project folder. It will be in the same directory as the settings.py file.

url(r'^api/musicians/$', MusicianListView.as_view()),
url(r'^api/musicians/(?P<pk>\d+)/$', MusicianView.as_view()),
url(r'^api/albums/$', AlbumListView.as_view()),
url(r'^api/albums/(?P<pk>\d+)/$', AlbumView.as_view()),

The output of the MusicianListView will be like this.

[
    {
        "id": 1,
        "first_name": "ganesh",
        "last_name": "xeno",
        "instrument": "Guitar"
    }
]

I also added few albums. Here is the sample output.

[
    {
        "id": 1,
        "artist": 1,
        "name": "NEO",
        "release_date": "2017-07-07",
        "num_stars": 4
    },
    {
        "id": 2,
        "artist": 1,
        "name": "NEO 2",
        "release_date": "2017-07-22",
        "num_stars": 4
    }
]

Now if we want the 'Albums' details in the MusicianListView, we have modify the MusicianSerializer as shown below.

from .models import *
from rest_framework import serializers, fields


class AlbumSerializer(serializers.ModelSerializer):

    class Meta:
        model = Album
        fields = ('id', 'artist', 'name', 'release_date', 'num_stars')


class MusicianSerializer(serializers.ModelSerializer):
    album_musician = AlbumSerializer(read_only=True, many=True)

    class Meta:
        model = Musician
        fields = ('id', 'first_name', 'last_name', 'instrument', 'album_musician')

Now the output of MusicianListView will be similar to the below code snippet.

[
    {
        "id": 1,
        "first_name": "ganesh",
        "last_name": "xeno",
        "instrument": "Guitar",
        "album_musician": [
            {
                "id": 1,
                "artist": 1,
                "name": "NEO",
                "release_date": "2017-07-07",
                "num_stars": 4
            },
            {
                "id": 2,
                "artist": 1,
                "name": "NEO 2",
                "release_date": "2017-07-22",
                "num_stars": 4
            }
        ]
    }
]

As we can see now the Albums are show as list. But here the point is we can only read the data.

PART 2:

Now let's get into the actual topic of our post. That is writing data to this nested serializer. So let's go ahead and modify serializer code as shown below.

from .models import *
from rest_framework import serializers, fields


class AlbumSerializer(serializers.ModelSerializer):

    class Meta:
        model = Album
        fields = ('id', 'artist', 'name', 'release_date', 'num_stars')


class MusicianSerializer(serializers.ModelSerializer):
    album_musician = AlbumSerializer(many=True)

    class Meta:
        model = Musician
        fields = ('id', 'first_name', 'last_name', 'instrument', 'album_musician')

    def create(self, validated_data):
        albums_data = validated_data.pop('album_musician')
        musician = Musician.objects.create(**validated_data)
        for album_data in albums_data:
            Album.objects.create(artist=musician, **album_data)
        return musician

    def update(self, instance, validated_data):
        albums_data = validated_data.pop('album_musician')
        albums = (instance.album_musician).all()
        albums = list(albums)
        instance.first_name = validated_data.get('first_name', instance.first_name)
        instance.last_name = validated_data.get('last_name', instance.last_name)
        instance.instrument = validated_data.get('instrument', instance.instrument)
        instance.save()

        for album_data in albums_data:
            album = albums.pop(0)
            album.name = album_data.get('name', album.name)
            album.release_date = album_data.get('release_date', album.release_date)
            album.num_stars = album_data.get('num_stars', album.num_stars)
            album.save()
        return instance

Sample data for creating and updating the MusicianSerializer is given below.

# Post data to the MusicianListView to create.
{
    "first_name": "ganesh",
    "last_name": "xeno",
    "instrument": "Guitar",
    "album_musician": [
        {
            "name": "NEO",
            "release_date": "2017-07-07",
            "num_stars": 5
        },
        {
            "name": "NEO 2",
            "release_date": "2017-07-22",
            "num_stars": 4
        }
    ]
}

# PUT data to the MusicianListView to update data.
{
    "id": 1,
    "first_name": "ganesh",
    "last_name": "xeno",
    "instrument": "Guitar",
    "album_musician": [
        {
            "id": 1,
            "artist": 1,
            "name": "NEO",
            "release_date": "2017-07-07",
            "num_stars": 5
        },
        {
            "id": 2,
            "artist": 1,
            "name": "NEO 2",
            "release_date": "2017-07-22",
            "num_stars": 4
        }
    ]
}

That is it for this article. Let's catch up on some other interesting topic. You can leave your comments if you have any doubts or feedback.

Comments

Recent Posts

Archive

2021
2020
2019
2018
2017
2016
2015
2014

Tags

Authors

Feeds

RSS / Atom