~/TechPurAI
~/tutorials/django-rest-framework/modelserializer
beginner·part 3 of 22·2 min read

ModelSerializer: the shortcut for model-backed APIs

Updated Aug 16, 2026Python · Django

TaskSerializer from part 2 repeated every field Django's Task model already declares. ModelSerializer generates that same field list from the model directly — the same relationship ModelForm has to Form in plain Django.

Rewriting it as a ModelSerializer

python
# tasks/serializers.py
from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ["id", "title", "description", "completed", "due_date", "owner", "created_at"]
        read_only_fields = ["owner", "created_at"]

Meta.fields lists which model fields to expose — explicit, the same reasoning as ModelForm in the Django series: never "__all__", since that silently exposes any field added to the model later, whether or not it should be client-visible. read_only_fields is the ModelSerializer-specific shortcut for fields that should appear in output but never be settable from input — owner gets assigned by the view (part 4), not chosen by whoever's sending the request.

What ModelSerializer infers automatically

Anything that needs different behavior than the model's own definition — a field that should be read-only, a custom validator (part 11) — still gets declared explicitly on the serializer class, exactly like the plain Serializer from part 2; ModelSerializer only removes the fields that don't need anything beyond what the model already says.

.save() — creating and updating directly

python
serializer = TaskSerializer(data={"title": "New task", "owner": 1})
serializer.is_valid()
task = serializer.save()          # creates a new Task row

serializer = TaskSerializer(existing_task, data={"title": "Updated title"}, partial=True)
serializer.is_valid()
task = serializer.save()          # updates existing_task in place

ModelSerializer adds .save() — something the plain Serializer from part 2 doesn't have, since it has no model to persist to. Passing an existing instance as the first argument alongside data= switches .save() from create to update; partial=True allows updating only some fields instead of requiring every one Meta.fields lists.

Common mistake

Listing owner in fields without also adding it to read_only_fields. Without that, any authenticated request can set any task's owner to any user ID it wants — the field being present in the serializer's output doesn't mean it should also be writable from input.

Next: an actual view, so this serializer has a request to work with instead of test data typed into a shell.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous2. Serializers: turning a Django model into JSONnext →4. Function-based API views with @api_view