In the previous post, we built a web API using pure Django to understand how APIs work under the hood. In this post, we will learn to build API in an easy way .i.e. by using the Django REST framework. It is a powerful and flexible toolkit for building Web APIs.

Why Django REST framework?
Django Rest Framework lets you create Restful API in a simple way. Some of the reasons to use Django REST framework are:
- Builtin features that follows don’t repeat yourself policies (DRY) such as serialization, pagination
- Authentication policies (OAuth)
- Web browsable api
- Great Documentation
Overview of Project
We will create an app that returns the blog posts of users in JSON with some additional features i.e. searching, pagination, and authentication.
We will implement a generic
view of rest_framework
. By the end of this post, we will create two API endpoints:
PostAPIList
(`/api/posts/`)
PostAPIList will return a query-set and POST a new blog post. It will also return search result of posts with ‘api/posts/?q= ‘PostAPIDetail
(`/api/post/id/
`)
PostAPIDetail will return a single blog post object, edit the blog post, and delete the blog post.
So now let’s go ahead and dive in.
Setup project
First and foremost create a project (drfapi) named as you like, inside a virtual environment (restapi).
- create an app called blog
- add the app inside INSTALLED_APP of settings.py
- migrate the builtin tables to database (SQLite)
- create a superuser to access the admin panel

If you are new to Django check my previous posts Getting Started with Django and Django Admin Panel.
Install Django REST framework
Install the following packages using pip inside virtual environment:
#terminal
pip install djangorestframework
pip install markdown # Markdown support for the browsable API.
pip install django-filter # Filtering support
Add 'rest_framework'
to your INSTALLED_APPS
setting.
#settings.py
INSTALLED_APPS = [
...
'blog',
'rest_framework',
]
Add the following to your root urls.py
file to use the browsable API and add the REST framework’s login and logout views.

#urls.py
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls')),
]
Create Post Model
Let’s create a model called Posts
with owner, content, image, updated, and timestamp fields.
#posts/models.py
from django.db import models
def upload_posts_image(instance, filename):
return "posts/{owner}/{filename}".format(owner=instance.owner, filename=filename)
class Posts(models.Model):
owner = models.ForeignKey('auth.User', related_name='posts', on_delete=models.CASCADE)
content = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to=upload_posts_image, null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.content)[:30]
Create Serializer class
First thing we need to get started on our Web API is to provide a way of serializing and deserializing the blog posts instances into representations such as json
.
We can do this by declaring serializers that work very similar to Django’s forms. Create a file inside the blog app called serializers.py.
#blog/serializers.py
from rest_framework import serializers
from blog.models import Posts
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Posts
fields = ['id', 'content','image','owner']
In the same way that Django provides both Form
classes and ModelForm
classes, REST framework includes both Serializer
classes, and ModelSerializer
classes. Here we use ModelSerializer from rest_framework.
And owner field is read-only and saves the user who is logged in.
Note: you can validation data in serializers class similar to forms
Add Authentication
We’d like all blog posts to be visible to anyone, but also make sure that only the user that created a post is able to update or delete it. To do that we’re going to need to create a custom permission.
In the blog app, create a new file, permissions.py
#blog/permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the posts.
return obj.owner == request.user
Write views
We will create two endpoints so two class is required .i.e. PostAPIList and PostAPIDetail
create a list view class as:
#blog/views.py
from .models import Posts
from .serializers import PostSerializer
from rest_framework import generics, permissions
from blog.permissions import IsOwnerOrReadOnly
class PostAPIList(generics.ListCreateAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
serializer_class = PostSerializer
def get_queryset(self):
qs = Posts.objects.all()
query = self.request.GET.get('q')
if query is not None:
qs = qs.filter(content__icontains=query)
return qs
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
The class PostAPIList works (/api/posts/) as:
- inherits ListCreateAPIView from generic class of rest_framework
- inherits built-in permissions from rest_framework (IsAuthenticatedOrReadOnly)
- uses the PostSerializer for serializing the data into JSON
- get_queryset function returns all the posts data. Also, filter the content based on query passed through URL.
`/?q=`
- prrform_create function saves the logged in users in the owner field of posts table
create a detail view class as:
class PostAPIDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly]
queryset = Posts.objects.all()
serializer_class = PostSerializer
The class PostAPIDetail works (/api/posts/<id>) as:
- inherits RetrieveUpdateDestroyAPIView from generic class of rest_framework
- inherits built-in permissions from rest_framework (IsAuthenticatedOrReadOnly). Also, inherits custom permissions (IsOwnerOrReadOnly) to make sure that only the user that created a blog post can update or delete it.
- uses the PostSerializer for deserializing JSON data that is used to save in database
Add pagination
Pagination is used to split the data across several pages. REST framework includes support for customizable pagination styles. This allows you to split large result sets into individual pages of data.
The pagination style may be set globally, using the DEFAULT_PAGINATION_CLASS
and PAGE_SIZE
setting keys. For example, to use the built-in limit/offset pagination, you would do something like this:
#settings.py
...
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
...
Map URLs
Each class in views.py represents an API endpoint such that there are two API endpoints.
Add a new urls.py file in the blog application directory
#blog/urls.py
from django.urls import path
from blog import views
urlpatterns = [
path('posts/', views.PostAPIList.as_view()),
path('posts/<int:pk>/', views.PostAPIDetail.as_view()),
]
Since the views are in class-based view, it is converted into function-based view using as_view().
Also include the app URLs in project URLs
#urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('blog.urls')),
...
]
Now you can test the API through browser by starting developing server.

(not authenticated)

(authenticated)

(not authenticated)

(authenticated)
Wrapping up
In this post, we learn to build the REST API using the Django REST framework.
Here is a GitHub link to code for Building API using Django REST framework.
You must be logged in to post a comment.