Paramclasses Academy - Blog

Tutorials Details

←
Post 23 of 29
→

Django REST Framework ViewSets and Routers: ModelViewSet, ReadOnlyModelViewSet, Automatic URL Routing, and API Development

Django REST Framework ViewSets and Routers: ModelViewSet, ReadOnlyModelViewSet, Automatic URL Routing, and API Development

October 21, 2026

Introduction

Django REST Framework (DRF) ViewSets and Routers make it easier to build and organize REST APIs. Instead of creating separate view classes and manually defining every URL, you can use ViewSets to group related API operations and Routers to generate URL patterns automatically.

A ViewSet combines related actions such as listing records, retrieving a single record, creating new records, updating existing records, and deleting records.

A Router automatically creates URL patterns for registered ViewSets, reducing repetitive code and making API development more organized.

In this module, you will learn how to build a Student Management API using ModelViewSet, ReadOnlyModelViewSet, and DRF Routers.

What You Will Learn

  • What ViewSets are in Django REST Framework.

  • What Routers do and why they are useful.

  • How to use ViewSet and ModelViewSet.

  • How to use ReadOnlyModelViewSet.

  • How to generate URLs automatically using DefaultRouter.

  • How to register ViewSets with a Router.

  • How to implement CRUD operations through one ViewSet.

  • How to configure serializers and querysets.

  • How to add authentication and permissions.

  • How to test API endpoints.

1. What Are ViewSets?

ViewSets are class-based components in Django REST Framework that group related API actions into one class.

In previous modules, you learned how to create separate generic views such as ListAPIView, CreateAPIView, UpdateAPIView, and DestroyAPIView.

With a ViewSet, related operations can be handled in a single class.

For example:

A Student Management API may need to:

  • Retrieve all students.

  • Retrieve one student.

  • Create a student.

  • Update student information.

  • Delete a student.

Instead of writing a separate view class for each operation, you can use ModelViewSet.

Benefits of ViewSets

  • Reduces repetitive code.

  • Groups related API operations.

  • Makes API structure easier to maintain.

  • Works with serializers and querysets.

  • Integrates with authentication and permissions.

  • Can be combined with Routers for automatic URL generation.

2. Understanding ModelViewSet

ModelViewSet is a ready-made DRF class that provides the standard CRUD operations for a model.

It includes the following actions:

Action HTTP Method Purpose
list GET Retrieve multiple records
retrieve GET Retrieve one record
create POST Create a new record
update PUT Fully update a record
partial_update PATCH Partially update a record
destroy DELETE Delete a record

These actions are implemented through DRF's existing generic view and mixin functionality.

3. Preparing the Student Model

This module uses a Student Management System.

Open students/models.py:

from django.db import models


class Student(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)
    course = models.CharField(max_length=100)

    def __str__(self):
        return self.name

Explanation:

  • name stores the student's name.

  • email stores a unique email address.

  • course stores the student's course.

  • __str__() returns the student's name when the object is displayed.

If this model already exists in your project, reuse it.

Otherwise, run:

python manage.py makemigrations
python manage.py migrate

4. Creating the Student Serializer

A Serializer converts model objects into API-compatible data and validates incoming request data.

Create or update students/serializers.py:

from rest_framework import serializers
from .models import Student


class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = ["id", "name", "email", "course"]
        read_only_fields = ["id"]

Explanation:

  • ModelSerializer automatically creates serializer fields based on the model.

  • model = Student connects the serializer to the Student model.

  • fields specifies which fields are included in the API.

  • read_only_fields prevents clients from setting the student ID directly.

5. Creating a ModelViewSet

Open students/views.py and add the following code:

from rest_framework import viewsets
from .models import Student
from .serializers import StudentSerializer


class StudentViewSet(viewsets.ModelViewSet):
    queryset = Student.objects.all().order_by("id")
    serializer_class = StudentSerializer

Understanding the code:

viewsets.ModelViewSet

Provides the standard list, retrieve, create, update, partial update, and delete actions.

queryset

Defines the records available to the ViewSet.

serializer_class

Specifies the serializer used to validate incoming data and format API responses.

With this small class, you have implemented the core CRUD behavior for the Student API.

However, you still need to configure the URLs.

6. What Is a Router?

A Router automatically generates URL patterns for registered ViewSets.

Without a Router, you would need to manually define URL patterns for listing, creating, retrieving, updating, and deleting students.

Using a Router makes this process simpler.

Django REST Framework provides two commonly used routers:

Router Purpose
SimpleRouter Generates standard URL patterns for ViewSets
DefaultRouter Generates standard URL patterns and includes an API root view

For this module, you will use DefaultRouter.

7. Configuring DefaultRouter

Create or update students/urls.py:

from rest_framework.routers import DefaultRouter
from .views import StudentViewSet

router = DefaultRouter()
router.register(
    r"students",
    StudentViewSet,
    basename="student"
)

urlpatterns = router.urls

Explanation:

  • DefaultRouter() creates the Router.

  • router.register() registers the Student ViewSet.

  • r"students" defines the URL prefix.

  • StudentViewSet handles the API actions.

  • basename="student" provides the base name used for generated URL names.

  • router.urls exposes the generated URL patterns.

Because the ViewSet defines a queryset, DRF can usually determine the basename automatically. Specifying it explicitly makes the configuration clear.

8. Connecting App URLs to the Main Project

Open your project's main urls.py, usually located beside settings.py.

For example, config/urls.py:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("students.urls")),
]

Make sure the import path matches your actual project structure.

Now run the development server:

python manage.py runserver

Open the API endpoint:

http://127.0.0.1:8000/api/students/

The DRF browsable API may display a page where you can view records and, depending on permissions and supported methods, submit API requests.

9. Understanding Automatically Generated URLs

When the Student ViewSet is registered with DefaultRouter, DRF generates routes for the supported actions.

Endpoint HTTP Method Operation
/api/students/ GET List students
/api/students/ POST Create a student
/api/students/1/ GET Retrieve student with ID 1
/api/students/1/ PUT Fully update student with ID 1
/api/students/1/ PATCH Partially update student with ID 1
/api/students/1/ DELETE Delete student with ID 1

The exact lookup value depends on your model and ViewSet configuration. In this example, the default lookup field is the model's primary key.

Important: These routes are generated by the Router. You do not need to write an individual path() for each standard CRUD action.

10. Testing the List API

Send a GET request to:

GET /api/students/

Example JSON response:

[
    {
        "id": 1,
        "name": "Rahul Sharma",
        "email": "rahul@example.com",
        "course": "Python"
    },
    {
        "id": 2,
        "name": "Priya Verma",
        "email": "priya@example.com",
        "course": "Django"
    }
]

The response contains a list of serialized student records.

If no student records exist, the endpoint normally returns an empty list.

11. Testing the Create API

Send a POST request to:

http://127.0.0.1:8000/api/students/

Example JSON request:

{
    "name": "Amit Kumar",
    "email": "amit@example.com",
    "course": "Django REST Framework"
}

If the submitted data is valid, DRF saves the student and typically returns HTTP 201 Created with the created record.

If validation fails, DRF normally returns HTTP 400 Bad Request with validation details.

Because the email field is unique, submitting an email that already exists will fail validation.

12. Testing Retrieve, Update, and Delete Operations

Retrieve a Student

Send:

GET /api/students/1/

This returns the student whose primary key is 1, if the record exists.

Update a Student

For a full update, send a PUT request:

PUT /api/students/1/

Example request body:

{
    "name": "Rahul Sharma",
    "email": "rahul@example.com",
    "course": "Advanced Django"
}

A PUT request normally requires all required editable fields.

For a partial update, use PATCH:

PATCH /api/students/1/

Example request body:

{
    "course": "Advanced Python"
}

PATCH updates only the supplied fields, subject to serializer validation.

Delete a Student

Send:

DELETE /api/students/1/

A successful deletion normally returns HTTP 204 No Content.

Important: Test DELETE requests using sample records in a development environment.

13. Understanding ReadOnlyModelViewSet

Sometimes users should be able to view records but must not create, update, or delete them.

For this purpose, DRF provides ReadOnlyModelViewSet.

Example:

from rest_framework import viewsets
from .models import Student
from .serializers import StudentSerializer


class StudentReadOnlyViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = Student.objects.all().order_by("id")
    serializer_class = StudentSerializer

This ViewSet supports:

  • GET collection: Retrieve multiple students.

  • GET detail: Retrieve one student.

It does not provide the standard create, update, or delete actions.

Practical example: A public course directory may allow visitors to view student or course information without giving them permission to modify records.

Remember that read-only behavior restricts the supported API actions; you should still configure permissions and ensure the displayed data is appropriate for the audience.

14. Using ReadOnlyModelViewSet with a Router

If you want to expose a read-only endpoint, register the read-only ViewSet with a Router.

For example:

from rest_framework.routers import DefaultRouter
from .views import StudentReadOnlyViewSet

router = DefaultRouter()

router.register(
    r"students",
    StudentReadOnlyViewSet,
    basename="student"
)

urlpatterns = router.urls

This generates the list and detail routes for the registered ViewSet.

Important: Do not register two ViewSets with the same URL prefix in the same Router unless you have deliberately designed a different routing arrangement. If you need both read-only and full CRUD behavior, choose one ViewSet for the prefix or use separate prefixes.

15. Customizing the Queryset

You can filter which records are returned by overriding get_queryset().

For example, create a ViewSet that returns students enrolled in courses containing the word “Python”:

from rest_framework import viewsets
from .models import Student
from .serializers import StudentSerializer


class PythonStudentViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = StudentSerializer

    def get_queryset(self):
        return Student.objects.filter(
            course__icontains="Python"
        ).order_by("name")

Register it using a different prefix if you want to keep it alongside the full Student API:

router.register(
    r"python-students",
    PythonStudentViewSet,
    basename="python-student"
)

Explanation:

  • filter() selects matching records.

  • course__icontains performs a case-insensitive partial match.

  • order_by("name") sorts records alphabetically.

  • get_queryset() allows the query to be customized dynamically.

This pattern is useful for search endpoints, category-based listings, and user-specific data.

16. Adding Authentication and Permissions

API endpoints should not automatically be available to everyone.

For example, the following ViewSet requires an authenticated user:

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Student
from .serializers import StudentSerializer


class StudentViewSet(viewsets.ModelViewSet):
    queryset = Student.objects.all().order_by("id")
    serializer_class = StudentSerializer
    permission_classes = [IsAuthenticated]

With this configuration, anonymous requests are denied unless authentication and permission settings are configured differently.

Security best practices:

  • Require authentication for private data.

  • Apply appropriate permissions to create, update, and delete operations.

  • Filter querysets when users should access only their own records.

  • Avoid exposing sensitive student information unnecessarily.

  • Test requests as both authenticated and unauthenticated users.

Important: Authentication confirms who the user is; permissions determine what that user is allowed to do.

17. ViewSets vs Generic Views

Feature Generic Views ViewSets
Structure Often uses separate classes for operations Groups related actions in a class
URL configuration Commonly uses explicit URL patterns Commonly uses Routers
CRUD implementation Uses specialized generic views ModelViewSet provides standard CRUD actions
Customization Customize individual views Customize ViewSet actions and querysets
Best use Specific or narrowly defined API operations Related API operations sharing one resource

When should you use Generic Views?

Use generic views when you want precise control over individual endpoints or only need a small subset of operations.

When should you use ModelViewSet?

Use ModelViewSet when a resource needs standard CRUD operations and you want to keep the API structure concise.

When should you use ReadOnlyModelViewSet?

Use it when the API should support only listing and retrieving records.

18. Common Errors and Solutions

Error 1: NameError: name 'StudentSerializer' is not defined

Solution: Import StudentSerializer from students.serializers.

Error 2: 404 Not Found

Solution: Check the URL prefix, registered Router, primary key, and whether the requested record exists.

Error 3: 405 Method Not Allowed

Solution: Verify that the selected ViewSet supports the HTTP method. For example, ReadOnlyModelViewSet does not provide standard POST, PUT, PATCH, or DELETE actions.

Error 4: ImproperlyConfigured or a basename-related error

Solution: Provide a unique basename when registering the ViewSet, particularly when the ViewSet does not define a class-level queryset.

Error 5: API URLs are not working

Solution: Check that router.urls is assigned to urlpatterns and that the project's main URL configuration includes the app's URLs.

Error 6: Duplicate URL names or overlapping routes

Solution: Review Router registrations and use clear, unique prefixes and basenames.

Error 7: Unauthorized requests

Solution: Check authentication credentials, permission classes, and the user's access rights.

Error 8: Duplicate email validation error

Solution: Use a unique email address or update the intended existing record.

19. Frequently Asked Questions (FAQs)

Q1. What is a ViewSet in Django REST Framework?

A ViewSet groups related API actions, such as listing, retrieving, creating, updating, and deleting records, into one class.

Q2. What is ModelViewSet?

ModelViewSet provides the standard CRUD actions for a model using a queryset and serializer.

Q3. What is a Router?

A Router automatically generates URL patterns for registered ViewSets.

Q4. What is the difference between SimpleRouter and DefaultRouter?

Both generate standard routes for ViewSets. DefaultRouter also provides an API root view.

Q5. What is ReadOnlyModelViewSet?

It provides list and retrieve actions without the standard create, update, and delete actions.

Q6. Do ViewSets automatically secure an API?

No. You must configure appropriate authentication, permissions, and queryset restrictions.

Q7. Can I customize a ModelViewSet?

Yes. You can override methods such as get_queryset(), customize serializers, configure permissions, and add custom actions when needed.

Q8. Do I need to write every URL manually when using a Router?

No. The Router generates the standard URL patterns for registered ViewSets.

20. Django REST Framework Interview Questions and Answers

Q1. What is the main advantage of ViewSets?

Answer: They group related API actions into one class, reducing repetitive code and improving organization.

Q2. What operations does ModelViewSet support?

Answer: List, retrieve, create, full update, partial update, and delete.

Q3. What is the purpose of DefaultRouter?

Answer: It generates URL patterns for registered ViewSets and provides an API root view.

Q4. What is the difference between ModelViewSet and ReadOnlyModelViewSet?

Answer: ModelViewSet provides standard CRUD actions, while ReadOnlyModelViewSet provides only list and retrieve actions.

Q5. What does router.register() do?

Answer: It registers a ViewSet with a URL prefix and optionally a basename so the Router can generate its URL patterns.

Q6. Why is basename useful?

Answer: It defines the base name used for generated URL names. It is especially important when the ViewSet does not define a class-level queryset.

Q7. What is the purpose of get_queryset()?

Answer: It determines which records a ViewSet can access and can apply filtering based on the request or user.

Q8. Does a Router replace serializers?

Answer: No. Routers generate URLs, ViewSets handle API actions, and serializers validate and represent data.

21. Practical Assignment

Build a Student Management REST API using ViewSets and Routers.

Requirements:

  1. Create a Student model with name, email, and course.

  2. Create a StudentSerializer.

  3. Implement a StudentViewSet using ModelViewSet.

  4. Register the ViewSet with DefaultRouter.

  5. Configure the main project URL to include the app's API URLs.

  6. Test GET requests for listing and retrieving students.

  7. Test POST requests for creating students.

  8. Test PUT and PATCH requests for updating students.

  9. Test DELETE requests using sample records.

  10. Create a read-only ViewSet using ReadOnlyModelViewSet.

  11. Add a filtered endpoint using get_queryset().

  12. Configure authentication and appropriate permissions.

  13. Test invalid data, missing records, and unauthorized requests.

Expected Result:

You should have a working REST API that uses ViewSets to group related operations and Routers to generate URL patterns automatically.

Conclusion

In this module, you learned how Django REST Framework ViewSets and Routers simplify API development. You explored ModelViewSet, ReadOnlyModelViewSet, DefaultRouter, automatic URL generation, CRUD operations, queryset customization, and permissions.

You also learned how to organize API endpoints efficiently and reduce repetitive code in a Student Management System.

In the next module, you will learn about Django REST Framework authentication, token-based authentication, permissions, and protecting API endpoints.

←
Post 23 of 29
→