How to create a REST API with Django REST framework

Zain Bagus
11 min readOct 23, 2023

Django REST framework (DRF) is a powerful and flexible toolkit for building web APIs. In this tutorial, we’ll learn how to build a CRUD API in just 15 minutes using the Django REST framework.

To build our sample to-do list application, we’ll start by setting up the Django REST framework in a Django project, followed by a complete tutorial on how to create a CRUD REST API with the Django REST framework.

Let’s get started!

What is Django?

Django is a high-level, open-source web framework for building dynamic and robust web applications. Developed using Python, Django simplifies the process of creating complex, data-driven websites by providing a well-structured, reusable, and extensible architecture.

At its core, Django follows the Model-View-Controller (MVC) architectural pattern, but it names the components differently: Model-View-Template (MVT). Here’s a breakdown of the key components:

  • Model: This is responsible for defining the data structure of your application. Django models allow you to define the database schema and perform database operations without writing SQL queries manually.
  • View: Views are responsible for handling the user interface and business logic. They process HTTP requests, retrieve data from models, and render templates to present the data to the user.
  • Template: Django templates provide an elegant way to create dynamic HTML, enabling you to insert data from your models into web pages. This separation of content and presentation ensures clean and maintainable code.

Django also offers a range of features and tools that simplify common web development tasks, such as user authentication, URL routing, form handling, and more. It includes an admin interface for easy content management and supports multiple database systems.

One of Django’s key strengths is its emphasis on security. It provides protection against common web vulnerabilities, such as SQL injection, cross-site scripting (XSS), and cross-site request forgery (CSRF). Additionally, Django encourages developers to follow best practices like the DRY (Don’t Repeat Yourself) and KISS (Keep It Simple, Stupid) principles.

Django’s extensive ecosystem includes various libraries, packages, and a vibrant community, making it a popular choice for web developers looking to build scalable, maintainable, and secure web applications. Whether you’re creating a simple blog or a complex, data-driven platform, Django’s flexibility and comprehensive documentation make it a valuable tool for web development.

What is a REST API?

A REST API, which stands for Representational State Transfer Application Programming Interface, is a set of rules and conventions for building and interacting with web services in a simple and standardized way. It is a fundamental component of web development that enables communication and data exchange between different software systems over the internet.

At its core, a REST API allows one software application to request and retrieve data or perform actions on another software application, typically using the HTTP protocol. Here are some key aspects that define what a REST API is:

  • Statelessness: A REST API is stateless, meaning that each request from a client to a server must contain all the information necessary to understand and fulfill that request. The server doesn’t store any information about the client’s state between requests, making it highly scalable and easy to manage.
  • Resources: In a REST API, resources are the core abstractions. These resources can be anything from data objects, services, or even specific functionalities. Each resource is identified by a unique URL, and clients can interact with them through standard HTTP methods like GET (to retrieve data), POST (to create new data), PUT (to update data), and DELETE (to remove data).
  • HTTP Methods: REST APIs make use of the standard HTTP methods to perform actions on resources. For example, a GET request retrieves data, while a POST request adds new data to a resource. The choice of HTTP method is determined by the desired action.
  • Representations: Data exchanged in a REST API is typically represented in a format such as JSON or XML. Clients and servers agree on a specific format for data, allowing for easy serialization and deserialization.
  • Stateless Communication: Clients interact with a REST API without needing to be aware of the server’s internal state. The server, in turn, processes each request independently.
  • Uniform Interface: REST APIs offer a consistent and uniform interface, making it easier for developers to understand and use them. This uniformity simplifies the creation and integration of web services.

REST APIs are widely used in modern web and mobile applications for various purposes, including retrieving data from a server, updating user profiles, interacting with social media platforms, and integrating with third-party services. They provide a straightforward and flexible way for different software components to communicate and share data, making them a crucial part of the internet’s infrastructure.

Why Django REST framework?

Django Rest Framework (DRF) is a powerful and popular framework for building web APIs in Python. There are several compelling reasons why one should consider choosing Django Rest Framework for their API development needs:

  • Robust and Mature: DRF is built on top of Django, a well-established and battle-tested web framework. This means you get the benefit of a mature and stable foundation, which is essential for large-scale or long-term projects.
  • Simplicity and Productivity: DRF simplifies the process of creating APIs by providing a wide range of built-in tools and features. It includes serializers for data validation and conversion, authentication and permissions systems, and view classes that make it easy to define the API’s behavior.
  • Flexibility: DRF allows you to build APIs in a highly flexible manner. You can create both simple and complex APIs, and you have the flexibility to customize and extend its functionality as per your project’s requirements.
  • Authentication and Authorization: DRF provides robust authentication and authorization mechanisms out of the box. It supports various authentication methods like token-based authentication, OAuth, and more. You can easily define fine-grained permissions to control who can access your API and what they can do.
  • Serialization: Serialization in DRF is a breeze. It allows you to convert complex data types, such as Django model instances, into Python data types and then render them into JSON or XML. This is particularly useful when building APIs that work with database records.
  • Documentation: DRF includes a browsable and self-updating API web interface that simplifies testing and documentation. This feature makes it easier for developers to understand and interact with your API.
  • Community and Ecosystem: DRF benefits from a large and active community. This means that you can find numerous third-party packages and resources to extend DRF’s functionality or troubleshoot any issues that may arise during development.
  • Scalability: DRF is designed to work well with Django, which can be scaled efficiently. You can take advantage of Django’s built-in support for database routing, caching, and load balancing to create scalable APIs.
  • Security: Django, upon which DRF is built, has a strong focus on security. Using DRF means inheriting these security features, making it a solid choice for applications that need to handle sensitive data.
  • Well-Documented: DRF has excellent documentation with numerous examples and guides. This makes it relatively easy for developers to learn and use effectively, even for those new to web API development.

In conclusion, choosing Django Rest Framework for your API development provides a robust, productive, and well-supported framework with a wealth of features and a strong emphasis on security. Whether you’re building a small internal API or a large-scale, production-ready system, DRF is a solid choice.

Setting up Django REST framework

Ideally, you’d want to create a virtual environment to isolate dependencies, however, this is optional. Run the command python -m venv django_env from inside your projects folder to create the virtual environment. Then, run source ./django_env/bin/activate to turn it on.

Keep in mind that you’ll need to reactivate your virtual environment in every new terminal session. You’ll know that it is turned on because the environment’s name will become part of the shell prompt.

Navigate to an empty folder in your terminal and install Django and Django REST framework in your project with the commands below:

pip install django
pip install django_rest_framework

Create a Django project called todo with the following command:

django-admin startproject todo

Then, cd into the new todo folder and create a new app for your API:

django-admin startapp todo_api

Run your initial migrations of the built-in user model:

python manage.py migrate

Next, add rest_framework and todo to the INSTALLED_APPS inside the todo/todo/settings.py file:

# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'todo_api'
]

Create a serializers.py and urls.py file in todo/todo_api and add new files as configured in the directory structure below:

├── todo
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
├── db.sqlite3
├── manage.py
└── todo_api
├── admin.py
├── serializers.py
├── __init__.py
├── models.py
├── urls.py
└── views.py

Be sure to include rest_framework and URLs as shown below in your main urls.py file:

# todo/todo/urls.py : Main urls.py
from django.contrib import admin
from django.urls import path, include
from todo_api import urls as todo_urls
urlpatterns = [
path('admin/', admin.site.urls),
path('api-auth/', include('rest_framework.urls')),
path('todos/', include(todo_urls)),
]

Next, create a superuser. We’ll come back to this later:

python manage.py createsuperuser

RESTful structure: GET, POST, PUT, and DELETE methods

In a RESTful API, endpoints define the structure and usage with the GET, POST, PUT, and DELETE HTTP methods. You must organize these methods logically.

To show how to build a RESTful app with Django REST framework, we’ll create an example to-do API. We’ll use two endpoints with their respective HTTP methods, as shown in the table below:

Creating models for our Django app

Let’s start by creating the model for our to-do list:

# todo/todo_api/models.py
from django.db import models
from django.contrib.auth.models import User

class Todo(models.Model):
task = models.CharField(max_length = 180)
timestamp = models.DateTimeField(auto_now_add = True, auto_now = False, blank = True)
completed = models.BooleanField(default = False, blank = True)
updated = models.DateTimeField(auto_now = True, blank = True)
user = models.ForeignKey(User, on_delete = models.CASCADE, blank = True, null = True)

def __str__(self):
return self.task

After creating the model, migrate it to the database.

python manage.py makemigrations
python manage.py migrate

Model serializer

To convert the Model object to an API-appropriate format like JSON, Django REST framework uses the ModelSerializer class to convert any model to serialized JSON objects:

# todo/todo_api/serializers.py
from rest_framework import serializers
from .models import Todo
class TodoSerializer(serializers.ModelSerializer):
class Meta:
model = Todo
fields = ["task", "completed", "timestamp", "updated", "user"]

Creating API views in Django

In this section, we’ll walk through how to create two API views, list view and detail view.

List view

The first API view class deals with the todos/api/ endpoint, in which it handles GET for listing all to-dos of a given requested user and POST for creating a new to-do. Notice that we’ve added permission_classes, which allows authenticated users only:

# todo/todo_api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from .models import Todo
from .serializers import TodoSerializer

class TodoListApiView(APIView):
# add permission to check if user is authenticated
permission_classes = [permissions.IsAuthenticated]

# 1. List all
def get(self, request, *args, **kwargs):
'''
List all the todo items for given requested user
'''
todos = Todo.objects.filter(user = request.user.id)
serializer = TodoSerializer(todos, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

# 2. Create
def post(self, request, *args, **kwargs):
'''
Create the Todo with given todo data
'''
data = {
'task': request.data.get('task'),
'completed': request.data.get('completed'),
'user': request.user.id
}
serializer = TodoSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

The GET() method first fetches all the objects from the model by filtering with the requested user ID. Then, it serializes from the model object to a JSON serialized object. Next, it returns the response with serialized data and status as 200_OK.

The POST() method fetches the requested data and adds the requested user ID in the data dictionary. Next, it creates a serialized object and saves the object if it’s valid. If valid, it returns the serializer.data, which is a newly created object with status as 201_CREATED. Otherwise, it returns the serializer.errors with status as 400_BAD_REQUEST.

Create an endpoint for the class-based view above:

# todo/todo_api/urls.py : API urls.py
from django.conf.urls import url
from django.urls import path, include
from .views import (
TodoListApiView,
)
urlpatterns = [
path('api', TodoListApiView.as_view()),
]

Run the Django server:

python manage.py runserver

Now, we’re ready for the first test. Navigate to http://127.0.0.1:8000/todos/api/. Make sure you’re logged in with your superuser credentials:

You can create a new to-do by posting the following code:

{
"task": "New Task",
"completed": false
}

Detail view

Now that we’ve successfully created our first endpoint view, let’s create the second endpoint todos/api/<int:todo_id> API view.

In this API view class, we need to create three methods for handling the corresponding HTTP methods, GET, PUT, and DELETE, as discussed above:

# todo/api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from todo.models import Todo
from .serializers import TodoSerializer
from rest_framework import permissions
# todo/todo_api/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework import permissions
from .models import Todo
from .serializers import TodoSerializer

class TodoListApiView(APIView):
# add permission to check if user is authenticated
permission_classes = [permissions.IsAuthenticated]

# 1. List all
def get(self, request, *args, **kwargs):
'''
List all the todo items for given requested user
'''
todos = Todo.objects.filter(user = request.user.id)
serializer = TodoSerializer(todos, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

# 2. Create
def post(self, request, *args, **kwargs):
'''
Create the Todo with given todo data
'''
data = {
'task': request.data.get('task'),
'completed': request.data.get('completed'),
'user': request.user.id
}
serializer = TodoSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

The GET() method first fetches the object with the ID todo_id and user as request user from the to-do model. If the requested object is not available, it returns the response with the status as 400_BAD_REQUEST. Otherwise, it serializes the model object to a JSON serialized object and returns the response with serializer.data and status as 200_OK.

The PUT() method fetches the to-do object if it is available in the database, updates its data with requested data, and saves the updated data in the database.

The DELETE() method fetches the to-do object if is available in the database, deletes it, and responds with a response.

Update the API urls.py as demonstrated below:

# todo/api/urls.py : API urls.py
from django.conf.urls import url
from django.urls import path, include
from .views import (
TodoListApiView,
TodoDetailApiView
)
urlpatterns = [
path('api', TodoListApiView.as_view()),
path('api/<int:todo_id>/', TodoDetailApiView.as_view()),
]

Now, if you navigate to http://127.0.0.1:8000/todos/api/<id>/, it will show the detail API view page. Notice that you correctly navigate to a valid ID. In the screenshot below, I used 7 as the ID:

Conclusion

Congratulations! You’ve successfully built your first fully functional CRUD Django REST API.

Building a RESTful API can be complicated, but Django REST framework handles complexity fairly well. I hope you have fun building new APIs using the Django REST framework, and be sure to leave a comment if you have any questions. Happy coding!

Get set up with LogRocket’s modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • NgRx middleware
  • Vuex plugin

PS: If you want to learn about working remotely, please check https://remoteworker.id

--

--

Zain Bagus
0 Followers

I am someone who is enthusiastic in the field of informatics engineering, I like to share the knowledge I have just gained