Tutorial Code

You can find the code for this tutorial here: https://github.com/appliku/drf-tutorial

What is RESTful API?

RESTful API (Representational State Transfer) is an architectural style for building web services that allows communication between different systems. RESTful APIs are designed to be simple and easy to use, allowing data to be transferred between different clients and servers.

A RESTful API works by using HTTP requests to GET, POST, PUT, and DELETE data. It uses a set of uniform resource identifiers (URIs) to identify resources and communicate with them.

Why we use RESTful API?

RESTful API is used to build scalable, efficient, and reliable web applications. Here are some reasons why we use RESTful API:

  • Simplicity: RESTful API uses a simple and lightweight architecture, making it easy to understand, implement, and maintain.

  • Scalability: RESTful API can handle a large number of client requests and can easily scale up or down depending on the traffic.

  • Flexibility: RESTful API is flexible and can work with different programming languages, platforms, and devices.

  • Statelessness: RESTful API does not store any client data or session information, making it easier to maintain and scale.

  • Security: RESTful API provides several security measures such as authentication, authorization, and encryption to secure data transmission and access.

  • Caching: RESTful API supports caching, which can improve performance and reduce server load.

  • Integration: RESTful API can be integrated with other systems and services, allowing for seamless communication and data exchange.

Overall, RESTful API is so popular because it offers a simple, flexible, and scalable architecture that can handle complex web applications and services.

The reason to have REST API in your project could be one of those: - Make a dashboard with a modern frontend framework, decoupled from your backend - Make a Command-Line Interface(CLI) tool to use your app - Integrate with other services, like Zapier, allowing people integrate with your app without coding - build additional backend services as part of your software, but decoupled from your main repo/codebase.

How to build REST API with Django?

Create a new Django project

Create a new Django project and start a new app called main.

mkdir rest_tutorial
cd rest_tutorial
python3 -m venv env
source env/bin/activate
pip install -U pip # update pip to the latest version
pip install django
pip install djangorestframework
django-admin startproject project .  # note the trailing '.' character

django-admin startapp main # create your main app

The project layout should look like this:

$ pwd

<some path>/rest_tutorial

$ find . -path './env' -prune -o -print # list all files and directories except `env`
.
./project
./project/asgi.py
./project/__init__.py
./project/settings.py
./project/urls.py
./project/wsgi.py
./manage.py
./main
./main/migrations
./main/migrations/__init__.py
./main/models.py
./main/__init__.py
./main/apps.py
./main/admin.py
./main/tests.py
./main/views.py

Apply migrations

python manage.py migrate

Create a superuser in non-interactive mode.

DJANGO_SUPERUSER_PASSWORD=Hello123 python manage.py createsuperuser --email admin@example.com --username admin --noinput

I prefer using non-interactive mode everywhere, because it also teaches how to automate things when it comes to deployment and other type of tasks, there is no user to input any data.

Create models for our TODO app

Open main/models.py in your favorite editor and add these models.

from django.contrib.auth import get_user_model
from django.db import models

User = get_user_model()


class TaskList(models.Model):
    name = models.CharField(max_length=200)
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='task_lists')

    def __str__(self):
        return self.name


class Task(models.Model):
    title = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    task_list = models.ForeignKey(TaskList, on_delete=models.CASCADE, related_name='tasks')
    is_done = models.BooleanField(default=False)

    def __str__(self):
        return self.title

This is a pretty simple TODO list models. List belongs to a user, tasks belong to the list.

Task can be marked as done.

This simple structure already gives us enough to show in terms of REST framework features.

Go to project/settings.py and add main and rest_framework to INSTALLED_APPS so it looks like this:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
        'rest_framework',  # new
        'main',  # new
]

Now run python manage.py makemigrations to create migration files for our app and its models.

Then run python manage.py migrate to apply these migrations.

Register new models in the admin.

Create a file main/admin.py:

from django.contrib import admin
from .models import TaskList, Task


class TaskListAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'owner')


admin.site.register(TaskList, TaskListAdmin)


class TaskAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'created_at', 'is_done', 'task_list')


admin.site.register(Task, TaskAdmin)

This code sets up the Django admin interface for the TaskList and Task models.

Two ModelAdmin classes, TaskListAdmin and TaskAdmin, are defined to customize the display of the TaskList and Task models in the Django admin interface. Each of these classes defines a list_display attribute that specifies which fields should be displayed in the list view for each model.

The admin.site.register method is then called twice to register the TaskList and Task models with their respective ModelAdmin classes. This makes the TaskList and Task models visible in the Django admin interface, and customizes their display based on the attributes defined in the corresponding ModelAdmin classes.

When this code is included in the admin.py file of a Django project, it sets up the necessary configuration for the Django admin interface to display the TaskList and Task models with the customizations defined in the TaskListAdmin and TaskAdmin classes, respectively. This allows admin users to easily view and manage the data stored in these models.

REST framework serializers

Create a file main/serializers.py.

Let's create serializers for the models we created.

from rest_framework import serializers
from . import models


class TaskListSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.TaskList
        fields = ('id', 'name',)


class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Task
        fields = ('id', 'title', 'created_at', 'is_done', 'task_list')

    def validate_task_list(self, value):
        if self.context['request'].user != value.owner:
            raise serializers.ValidationError('Invalid task list')
        return value

Let's talk for a second about what is serializers.

Serializer is a layer that helps to convert data into a representation and back while also validating the data.

Think of it as what Django forms do, but for API. At least that's what helped me to understand serializers initially.

In our example we have a validation for task_list during creation/update of the Task. TaskSerializer will check if the task_list specified for the Task belongs to the user who is sending the request.

If the user is different then ValidationError will be raise and the user will receive the 400 error.

REST framework CRUD views

We will use ModelViewSet for our TODO list app.

In Django, a ModelViewSet is a class that combines the functionality of a ViewSet with the abilities of Django's Model class. ModelViewSet provides a set of CRUD operations (Create, Read, Update, and Delete) that can be performed on a particular Django model.

When you define a ModelViewSet, it automatically generates a set of URLs for those CRUD operations. This can save you time and effort in setting up the URLs for your views.

ModelViewSet is part of Django Rest Framework (DRF), a powerful toolkit that allows you to easily build APIs in Django. It is particularly useful for building RESTful APIs, where you want to expose your data as a resource.

Using a ModelViewSet can save you time and effort in writing repetitive code for your views. For example, if you have a model called 'Product', you can create a ModelViewSet for it, which will provide you with a set of CRUD operations that can be performed on the 'Product' model. You can then customize these operations to suit your specific needs.

ModelViewSet is good to use when you are building APIs and you want to provide CRUD functionality on a particular Django model. It is also useful when you want to create a consistent set of URLs for your views, and when you want to reduce the amount of code you need to write.

In summary, you should use a ModelViewSet when you want to create a RESTful API for a particular Django model, and you want to reduce the amount of code you need to write for your views.

I prefer to decouple regular template rendering views from API views by putting them in separate files or modules.

Thus, I suggest you create a file main/api.py and let's create our API views in there.

from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import ModelViewSet

from main.serializers import TaskListSerializer, TaskSerializer
from . import models


class TaskListViewSet(ModelViewSet):
    serializer_class = TaskListSerializer
    permission_classes = [IsAuthenticated, ]

    def get_queryset(self):
        return models.TaskList.objects.filter(owner=self.request.user)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)


class TaskViewSet(ModelViewSet):
    serializer_class = TaskSerializer
    permission_classes = [IsAuthenticated, ]

    def get_queryset(self):
        return models.Task.objects.filter(task_list__owner=self.request.user)

Viewsets TaskListViewSet and TaskViewSet are used to handle CRUD operations for TaskList and Task models respectively.

Both viewsets inherit from ModelViewSet, which provides default implementations for HTTP methods such as GET, POST, PUT, DELETE, etc.

TaskListViewSet specifies the TaskListSerializer as the serializer class to use for serializing and deserializing TaskList model instances. It overrides the get_queryset method to return a filtered queryset that only includes TaskList objects that belong to the current authenticated user. The perform_create method is also overridden to set the user field of the TaskList instance being created to the current authenticated user.

TaskViewSet specifies the TaskSerializer as the serializer class to use for serializing and deserializing Task model instances. It overrides the get_queryset method to return a filtered queryset that only includes Task objects that belong to a TaskList that belongs to the current authenticated user.

Django REST framework router and URLs

Now let's create URLs for our API.

Create a file main/urls.py.

from rest_framework import routers

from .api import *

router = routers.DefaultRouter()
router.register(r'task-lists', TaskListViewSet, basename='task-lists')
router.register(r'tasks', TaskViewSet, basename='tasks')

urlpatterns = router.urls

The routers module is imported from the rest_framework package.

A DefaultRouter object is created and assigned to the router variable. The register method of the router object is then called twice to register two viewsets for their respective endpoints. The first endpoint is /task-lists and it maps to the TaskListViewSet viewset, while the second endpoint is /tasks and it maps to the TaskViewSet viewset.

Finally, the urlpatterns variable is set to the router.urls attribute, which is a list of URL patterns that correspond to the registered viewsets.

When this code is included in the urls.py file of a Django project, it sets up the necessary routing for the two viewsets to handle HTTP requests to the specified API endpoints. For example, a GET request to /task-lists would be handled by the TaskListViewSet's list method, while a POST request to /tasks would be handled by the TaskViewSet's create method.

Now open the project/urls.py and let's include our API routes into the project's URLs.

from django.contrib import admin
from django.urls import path, include  # new: added include function

urlpatterns = [
    path('', include('main.urls')),  # new
    path('admin/', admin.site.urls),
]

Testing our API

Let's run our app.

python manage.py runserver

Open our app in browser:

http://127.0.0.1:8000/

Django REST Framework Index Page

You see two links to our end points.

First, you need to authenticate, in order to do that let's visit the admin URL and do it there.

https://127.0.0.1:8000/admin/

Use credentials we used earlier: "admin", "Hello123"

Visit endpoint: http://127.0.0.1:8000/task-lists/

Create a list.

After submitting the form you will see that list was created

Visit endpoint for task lists again and see that we have a list:

http://127.0.0.1:8000/task-lists/

Now if you go to the admin, to Task Lists you will see that the object was created with the owner field set to "admin".

http://127.0.0.1:8000/admin/main/tasklist/

Let's create a task.

Head over to the tasks endpoint.

http://127.0.0.1:8000/tasks/

Fill in the form and create a task.

After clicking "POST" you will see that the Task record was created.

Congratulations!