DEV Community

Cover image for Introduction to serializers in Django Rest Framework (DRF) Part II
Prabodh Tuladhar
Prabodh Tuladhar

Posted on

Introduction to serializers in Django Rest Framework (DRF) Part II

In Part I of the tutorial, we built serializers from scratch.

We defined a ProductModel with the fields name, description, price, stock, is_active. We then typed every single one of these fields again when we defined the ProductSerializer.

The pictures shows the codes of models and serializer class with repetitive fields

Repetitive Code in ProductModel and ProductSerializer

Enter ModelSerializer.

In this article, we are going to take the code we wrote in Part I and use ModelSerializer to reduce the repetitive code.

What is a ModelSerializer?

A ModelSerializer is a shortcut that automatically creates a serializers class with fields that are in the model class.

When we use a ModelSerializer, DRF inspects the model and automatically does these things:

  1. Generates fields from the model so you don't have to
  2. Automatically adds field validations. Your model may have rules such as unique=True, ModelSerializer reads these rules and implements them automatically
  3. Implements create() and update() methods. A ModelSerializer knows which model to use and how to update and create it. You can override create() and update() methods if you need customized behaviors.

Let's look at the ProductModel we created in Part I

# demo/models.py

from django.db import models

class ProductModel(models.Model):
    name = models.CharField(max_length=150)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField()
    is_active = models.BooleanField(default=True)

    def __str__(self):
        return self.name
Enter fullscreen mode Exit fullscreen mode

and here was the ProductSerializer associated with the model

#demo/serializers.py

from rest_framework import serializers
from .models import ProductModel

class ProductSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=150)
    description = serializers.CharField()
    price = serializers.DecimalField(max_digits=10, decimal_places=2)
    stock = serializers.IntegerField()
    is_active = serializers.BooleanField()

    # We must implement this to support saving data
    def create(self, validated_data):
        # We unpack the validated data and create a model instance
        return ProductModel.objects.create(**validated_data)
Enter fullscreen mode Exit fullscreen mode

Now lets rewrite the above code using ModelSerializer.

The updated code should look something like this

#demo/serializers.py

from rest_framework import serializers
from .models import ProductModel

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductModel
        fields = ['name', 'description', 'price', 'stock', 'is_active']
Enter fullscreen mode Exit fullscreen mode

Now lets take a look at what we changed. The first change is the ProductSerializer class inherits from serializer.ModelSerializer instead of the base serializer class.

Another important difference is the lack of fields corresponding to ProductModel, instead we have given the name of the model and ModelSerializer automatically generates the fields based on this model. Also we have specified which fields to generate in the fields attribute

The image shows the a part of code for serializer dot pie

ProductSerializer inherits from ModelSerializer

But wait, we have written the models and fields inside another class called Meta. Let's also be talk about this class.

Meta class is an inner class since we have defined the class inside a pre existing class.

The image shows the a part of code for serializer dot pie

models and fields attributes defined inside class Meta

Think of the Meta class as Configuration Settings for the serializer.

In the regular serializer, we built all the fields by ourselves so we did not need this inner class, but for the ModelSerializer we are asking DRF to build all the fields for us, so it needs to know from which model to build the field and what to include in the fields.

We write all of these configurations in the Meta class.

If we forget to write the Meta class and only specify the models and fields, Python will throw an AssertionError.

We can also replace the list of fields with __all__, though writing all the fields explicitly is a better practice.

#demo/serializers.py

from rest_framework import serializers
from .models import ProductModel

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProductModel
        # fields = ['name', 'description', 'price', 'stock', 'is_active']
        # we can write __all__ instead of typing name of all the fields
        fields = '__all__'
Enter fullscreen mode Exit fullscreen mode

Now lets open up the python shell to see if this works or not. Make sure your python virtual environment where you have installed Django and Django Rest Framework is active.

python manage.py shell
Enter fullscreen mode Exit fullscreen mode

First lets import our models and serializers

from demo.models import ProductModel
from demo.serializers import ProductSerializer
Enter fullscreen mode Exit fullscreen mode

Step 1: Create a dummy data

product = ProductModel.objects.create(
    name='Asus Zenbook 14',
    description='Asus ZenBook 14 I7 11th Gen | 16GB RAM | 512GB SSD',
    price=110.99, 
    stock=72, 
    is_active=True
)
Enter fullscreen mode Exit fullscreen mode

Step 2: Serialize the data

Now we pass the object to the serializer and print the serializer

serializer = ProductSerializer(product)
print(serializer.data)
Enter fullscreen mode Exit fullscreen mode

When we print this we get the following output

Output:

{
    'id': 1,
    'name': 'Asus Zenbook 14',
    'description': 'Asus ZenBook 14 I7 11th Gen | 16GB RAM | 512GB SSD',
    'price': 110.99,
    'stock': 72,
    'is_active': True
}

Enter fullscreen mode Exit fullscreen mode

Step 3: Deserialize the data

Now lets deserialize the data as well. Imagine as before, a user has added another product. This data is represented as JSON and we must save this data to the database.

Lets assume this is the incoming data

{
    "name": "Dell XPS 13",
    "description": "Dell XPS 13 I7 12th Gen | 16GB RAM | 1TB SSD",
    "price": 1499.99,
    "stock": 45,
    "is_active": True
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Save the data

Now lets deserialize the data and save it to the database

data = {
    "name": "Dell XPS 13",
    "description": "Dell XPS 13 I7 12th Gen | 16GB RAM | 1TB SSD",
    "price": 1499.99,
    "stock": 45,
    "is_active": True
}
serializer = ProductSerializer(data=data)
Enter fullscreen mode Exit fullscreen mode

As before we will print the data after checking the validity of the data using the is_valid() method

if serializer.is_valid():
    print(serializer.data)
else:
    print(serializer.errors)
Enter fullscreen mode Exit fullscreen mode

We get the following output.
Output:

{
    'name': 'Dell XPS 13',
    'description': 'Dell XPS 13 I7 12th Gen | 16GB RAM | 1TB SSD',
    'price': '1499.99',
    'stock': 45,
    'is_active': True
}
Enter fullscreen mode Exit fullscreen mode

This code has deserialized the data and to save this deserialized data we use the use the serializer.save() method

if serializer.is_valid():
    print(serializer.data)
    # save the deserialized data
    serializer.save()

else:
    print(serializer.errors)
Enter fullscreen mode Exit fullscreen mode

We can see that this data has been saved in the database by running this query.

stored_data = ProductModel.objects.filter(name='Dell XPS 13')
print(stored_data)
Enter fullscreen mode Exit fullscreen mode

This code gives the following output:

Output:

<QuerySet [<ProductModel: Dell XPS 13>]>
Enter fullscreen mode Exit fullscreen mode

Here we need to understand another important fact about ModelSerializer. Previously in our base Serializer, we had to implement the create() method ourselves but in the ModelSerializer the create() method has already been implemented.

This allows us to call the save() method. In addition to automatic implementation of create() method, ModelSerializer also automatically implements update() method.

Peeking Under the Hood

1. Checking the fields

We have seen that ModelSerializer has automatically generated the serializers fields and automatically generated the create() and update() method.

We can actually ask Python to show you what code DRF generated for you. In your shell, type:

print(repr(serializer))
Enter fullscreen mode Exit fullscreen mode

This gives output:

Output:

The image shows the automatically created serializer fields

We can see that DRF has automatically created all these fields automatically based on our model.

In this two-part series, I have tried to demystify the Django Serializer.

Part I showed us that serializers are just translators between Python Objects and JSON.
Part II showed us how ModelSerializer automates the boring parts of that translation.

Now that you have your data serialized, the next step is to figure out how to send it to the user through URLs and Views. But that is a story for another article.

We have covered a lot of ground in this two-part series.

  • In Part I, we dismantled the concept of serialization, built classes manually, and wrote our own validation logic. This was crucial for understanding how DRF works under the hood.

  • In Part II, we leveraged the power of ModelSerializer to eliminate boilerplate: We deleted 90% of our code while keeping the same and automate logic creation to some extent.

So, which one of the serializers should you use?

As a rule of thumb:

  • Stick to ModelSerializer for standard resources (like Users, Products, Orders) where your API mirrors your database. This will be 95% of your work.
  • Keep the base Serializer in your back pocket for non-database actions, such as handling a Login request, validating a contact form, or processing a file upload that goes to AWS S3 instead of your database.

References

Serializers - Django Rest Framework
Serializer Tutorial - Django Rest Framework

Top comments (0)