Mastering Django REST Framework: An In-depth Guide to Serializers

Ukeme Wilson
5 min readJul 13, 2024

--

In the world of web development, creating robust and efficient APIs is crucial for seamless data exchange between the server and client applications.

At the heart of Django REST Framework (DRF) lies the concept of serializers, which serve as the bridge between complex data types and easily consumable JSON or XML formats.

In this article, we will delve into the intricacies of DRF serializers, exploring their features, types, and practical use cases to help you build more effective and maintainable APIs.

Serializers helps in converting complex data types, such as querysets and model instances, into native Python datatypes that can then be easily rendered into JSON, XML, or other content types. They also handle deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data

Consider this analogy: You work at a mailing company, and a little child wants to write an essay to the president of an organization. The child uses pencils and crayons to scribble down some words, which are not very clear or logical. You, the mailman, rewrite what you think the child means, making it wordy and logical before passing it to the president.

The president reads it and decides to reply to the child, but his response is too complex for the child to understand. So, you, the mailman, get a pencil and crayons and try to describe to the child what the president responded.

In this analogy:

✔The child represents the client making a request.

✔The president represents the server processing the request and generating a response.

✔The mailman represents the DRF serializers handling the conversion of data between the client and server.

✔The scribbled words represent the raw unstructured data coming from the client

✔The complex response represents a Django model instance or a complex query result

Types of Serializers

a. Serializer

b. ModelSerializer

c. HyperlinkedModelSerializer

d. ListSerializer

e. BaseSerializer

To create a basic serializer one needs to import the serializers class from rest_framework.

from rest_framework import serializers

A. serializers.Serializer

This is the most basic class for creating serializers. It builds on top of BaseSerializer. It provides more control and flexibility but requires explicit field definitions and handling.

It can be used for non-model data or to customize the serialization behavior. It serves as the base class for defining custom serializers.

You define the fields you want to include in the serialized output.

# models.py
from django.db import models

class Book(models.Model):
title = models.CharField(max_length=50)
pages = models.IntegerField()
best_seller = models.BooleanField(default=True)

def __str__(self) -> str:
return self.title


# serializers.py
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=50)
pages = serializers.IntegerField()
best_seller = serializers.BooleanField(default=True)

def create(self, validated_data):
return Book.objects.create(**validated_data)

def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.pages = validated_data.get('pages', instance.pages)
instance.best_seller = validated_data.get('best_seller', instance.best_seller)

instance.save()

The id field is read-only, meaning it cannot be set during deserialization.

book_data = {
'title': 'A nice title',
'pages': 300,
'best_seller': False
}

serializer = BookSerializer(data=book_data)

if serializer.is_valid():
book = serializer.save()
print(book.id)
else:
print(serializer.errors)

When you instantiate BookSerializer with data=book_data, it prepares the data for validation.

Calling is_valid() runs the validation logic, checking that all required fields are present and valid.

If the data is valid, serializer.save() creates or updates a User instance based on the validated data.

Optional Custom Methods:

· create: Define how to create a new instance when saving deserialized data.

· update: Define how to update an existing instance when saving deserialized data.

If you are using serializers.Serializer for read-only operations (e.g., just to serialize data for an API response), you do not need to define create and update. However, if you intend to use the serializer for creating or updating objects based on incoming data, you will need to define these methods to specify how the data should be transformed into model instances.

field-level validation: When performing field-level validation in a serializers.Serializer, the method name must follow the specific naming convention validate_<field_name> where <field_name> is the name of the field you want to validate. This allows Django REST Framework (DRF) to automatically call this method when validating that field.

def validate_pages(self, value):
if value < 3:
raise serializers.ValidationError("The Number of pages is too small.")
return value

object level validation: To perform object level validation on the entire data, you can validate instead.

def validate(self, data):
if data['pages'] < 3:
raise serializers.ValidationError("The Number of pages is too small.")
return data

B. serializers.ModelSerializer

This serializer provides a shortcut for creating serializers that deal with model instances and querysets. It automatically generates a set of fields and simple validation rules for you.

It automatically creates a Serializer class with fields that correspond to the Model fields and it includes methods to create and update model instances.. It is a subclass of serializers.Serializer

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'pages', 'best_seller']

Attributes of the Meta class:

field: field attribute in the Meta calls can be specified as a list, a tuple or using the special value __all__

fields = ['id', 'title', 'pages', 'best_seller']
# or
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
# or
fields = "__all__"

exclude: Specifies the fields to exclude from the serializer.

exclude = ('best_seller',)

read_only_fields: Specifies fields that should be read-only, meaning they will be included in the serialized output but not in the deserialized input.

read_only_fields = ('id',)

extra_kwargs: A dictionary of additional keyword arguments to apply to specific fields. This can be used to set attributes such as required, read_only, validators, etc., on individual fields

extra_kwargs = {
'best_seller': {'required': False}
}

C. serializers.HyperlinkedModelSerializer

This extends the functionality of the standard ModelSerializer. Unlike ModelSerializer, which uses primary keys to represent relationships, HyperlinkedModelSerializer uses hyperlinks to represent relationships between resources.

Automatically generates URLs for each resource based on the URL patterns defined in your Django application.

from rest_framework import serializers


class BookSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'pages', 'best_seller']

D. serializers.ListSerializer

This handles serialization and deserialization of lists of objects. It supports batch operations like creating, updating, and deleting multiple objects at once.

from rest_framework import serializers


class BookListSerializer(serializers.ListSerializer):
child = BookSerializer()

def create(self, validated_data):
items = [Book(**book) for book in validated_data]
return Book.objects.bulk_create(items)

def update(self, instance, validated_data):
book_mapping = {book.id: book for book in instance}
data_mapping = {book_data['id']: book_data for book_data in validated_data}

ret = []
for book_id, data in data_mapping.items():
book = book_mapping.get(book_id, None)
if book is not None:
ret.append(self.child.update(book, data))
else:
ret.append(self.child.create(data))

return ret

Conclusion

In this article, we explored the fundamentals of DRF serialization, including the purpose of serializers, the types of serializers, how to create them, and various features like validation and custom serialization logic.

We’ve covered the basics and some advanced features, but there’s always more to learn. Consider diving deeper into custom serializers and performance optimization to further enhance your skills.

The best way to solidify your understanding of DRF serialization is through hands-on practice.

Let’s Get in Touch! Follow me on:

>GitHub: @bosukeme

>Linkedin: Ukeme Wilson

You can read other of my Medium Articles here

--

--