Hello! Welcome to our next lesson.
In our last session, we explored the fundamentals of serialization using the base serializers.Serializer class. You learned how to manually define fields to convert Python objects to JSON and how to deserialize and validate incoming data. This was crucial for understanding the mechanics, but you may have noticed it involved writing code that duplicated information already present in our models.
Today, we'll build directly on that knowledge to learn a more efficient and powerful approach. The learning outcome for this lesson is to create a ModelSerializer to automatically serialize model instances. This class is a cornerstone of DRF, designed to work seamlessly with Django's ORM, dramatically reducing the amount of code you need to write.
By leveraging ModelSerializer, you'll be one step closer to rapidly building the robust REST API you're aiming for.
The Problem: Code Repetition
Let's revisit the core idea from our last lesson. If we have a Django model, say for a blog post:
# in models.py
from django.db import models
class Post(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100)
content = models.TextField()
author = models.CharField(max_length=100)
class Meta:
ordering = ['-created_at']
And we want to serialize it using the base Serializer class we learned about previously, our serializers.py would look like this:
# in serializers.py
from rest_framework import serializers
class PostSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=100, required=True)
content = serializers.CharField()
author = serializers.CharField(max_length=100)
created_at = serializers.DateTimeField(read_only=True)
Notice the redundancy. We've defined title, content, and author with their types and constraints in models.py, and now we're doing it all over again in serializers.py. This violates the "Don't Repeat Yourself" (DRY) principle and can lead to bugs if you update the model but forget to update the serializer.
This is precisely the problem that ModelSerializer solves.

The ModelSerializer Solution
In the same way that Django has forms.Form and forms.ModelForm, DRF has serializers.Serializer and serializers.ModelSerializer. The ModelSerializer is a specialized class that can automatically generate a set of fields for you based on a given model.
To learn about this powerful shortcut, let's start with the official documentation.
The DRF documentation gives the most authoritative and concise explanation of ModelSerializer. Focus on the refactoring example it presents.
Read the section titled 'Using ModelSerializers'. Pay close attention to how the verbose SnippetSerializer is transformed into a much more compact version using ModelSerializer and an inner Meta class.
As you saw in the documentation, the core of a ModelSerializer is its inner Meta class, which is used to configure the serializer. Let's see a live demonstration of this refactoring process.
Django REST Framework Tutorial | Serialization
This video provides an excellent visual demonstration of converting a verbose Serializer into a concise ModelSerializer, reinforcing what you just read.
Watch the video from 20:24 to 22:15. The instructor starts with a serializer that explicitly defines all fields (like our example above) and then refactors it using ModelSerializer. Notice how much code is removed.
Dissecting the Meta Class
After that refactoring, our PostSerializer would look like this:
# in serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'created_at']
This is much cleaner and more maintainable! The Meta class needs two main attributes:
model: The Django model that the serializer should be based on (in this case,Post).fields: A list of field names from the model that should be included in the serialized output.
Specifying Fields with fields and exclude
DRF gives you flexibility in how you specify fields.
-
fields = [...]: An explicit list of fields to include. This is good for security and clarity, as it ensures you only expose the data you intend to. -
fields = '__all__': A convenient shortcut to include all fields from the model. This is useful for quick prototyping or when you truly want to serialize the entire model.
The following video demonstrates this '__all__' shortcut in a slightly different project.
Django Rest Framework | Serializers & CRUD
This video from Dennis Ivy shows how to quickly create a ModelSerializer for a Task model, making use of the fields = '__all__' option.
Watch from 08:05 to 10:13. The focus here is on the creation of serializers.py, defining the TaskSerializer, and using class Meta with fields = '__all__' to include every field from the Task model automatically.
exclude = [...]: Alternatively, you can specify a list of fields to exclude from the serialization. This is useful when you want to include almost all fields from a model, with just a few exceptions. For example:exclude = ['created_at']. You cannot declare bothfieldsandexclude.
Test your understanding!
You have the following Django model for tracking inventory:
class Product(models.Model):
sku = models.CharField(max_length=50, unique=True)
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
stock_level = models.IntegerField()
last_updated = models.DateTimeField(auto_now=True)
Write a ProductSerializer class using ModelSerializer that serializes all fields except for last_updated.
Show answer
There are two correct ways to do this.
Using fields:
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ['id', 'sku', 'name', 'description', 'stock_level']
Using exclude (which is more concise in this case):
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
exclude = ['last_updated']
What ModelSerializer Does Under the Hood
You mentioned you're interested in Django's internal workings, so it's important to understand that ModelSerializer is not magic. It's simply a convenient shortcut that does the following for you automatically:
- Generates Fields: It inspects the
modeland creates a corresponding serializer field for each model field specified (e.g.,models.CharFieldbecomesserializers.CharField). - Adds Validators: It automatically includes validators based on the model definition, such as
max_lengthfor aCharFieldorunique=True. - Implements
create()andupdate(): It provides default implementations for the.create()and.update()methods, which are used to save instances to the database. We saw these methods defined manually in the last lesson;ModelSerializerhandles them for you.
You can prove this to yourself by inspecting a serializer instance in the Django shell. The repr() function gives you a detailed look at the fields it has generated.
# In the Django shell: python manage.py shell
>>> from your_app.serializers import PostSerializer
>>> serializer = PostSerializer()
>>> print(repr(serializer))
PostSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(max_length=100)
content = CharField(style={'base_template': 'textarea.html'})
author = CharField(max_length=100)
created_at = DateTimeField(read_only=True)
Even though we didn't declare these fields ourselves, ModelSerializer generated them for us, complete with attributes like max_length and read_only inferred directly from the Post model.
Conclusion
Great job! You've now mastered one of the most significant time-saving features in Django REST Framework. By using ModelSerializer, you can create robust, maintainable serializers that are tightly integrated with your Django models, all while writing significantly less code.
Key Takeaways:
ModelSerializerautomatically generates fields for a serializer by inspecting a Django model.- This is done by defining an inner
Metaclass and setting themodelandfields(orexclude) attributes. - It reduces code duplication and the risk of inconsistencies between your models and serializers.
- Under the hood, it generates fields, validators, and default
.create()and.update()methods, making it the standard choice for serializing model instances.
Preview of the Next Lesson:
We've now seen how to define a ModelSerializer, but we haven't yet used it to handle incoming data for creating or updating objects in a real API view. In the next lesson, we will focus on exactly that: using serializers to validate incoming data for create and update operations. We will build API endpoints that accept POST and PUT requests, use our ModelSerializer to validate the data, and save it to the database.