Skip to main content

How to create REST API using Django REST Framework


This post begins with already working project and app's, I found that there some few requirement's
that my project needed to handle and the best option for those requirement's was to use the Django's  Rest Framework. The way that I will tackle this task is more specific to the needs of the project rather than a one to one how to..., that being said you can still follow along, the approach that I'm going to use is easy to follow since I'll be providing a lot of information a log the way for better understanding of the why and how.....this code is available on Github, enough with the alerts and on with the show.

Note: If you would want to mimic the exactly settings then you will need to enable user authentication on your project you can follow this link for details

Start with the DRF (Django Rest Framework) installation

pip3 install djangorestframework

For our app to use DRF, we'll have to add rest_framework into our settings.py. 

nano bio_metric/settings.py

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

My model look's like this

nano base/models.py

from django.conf import settings
from django.db import models
from django.utils import timezone


class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    text = models.CharField(max_length=200)
    created_date = models.DateTimeField(default=timezone.now)

"""Return a human readable representation of the model instance."""
    def __str__(self):
        return self.title


Serialization
A Serializer transforms model instances into JSON or XML. This the real great function that Django Rest Framework provides for us since ultimately a web API endpoint returns data we can understand, like JSON or XML and available HTTP verbs.


Adding endpoints to the Post and User models

nano base/serializers.py

from django.contrib.auth.models import User
from rest_framework import serializers
from base.models import Post



class PostSerializer(serializers.ModelSerializer):
    author = serializers.ReadOnlyField(source='author.username') 
    class Meta:
        model = Post
        fields = ('id', 'title', 'text', 'author', )


class UserSerializer(serializers.ModelSerializer):
    posts = serializers.PrimaryKeyRelatedField(
        many=True, queryset=Post.objects.all())

    class Meta:
        model = User
        fields = ('id', 'username', 'posts')


Since posts is a reverse relationship on the default Django User model, it will not be included by default using the ModelSerializer class, we need to add an explicit field for it.

nano base/views.py

from django.conf import settings
from django.utils import timezone
from django.shortcuts import render, get_object_or_404
from .models import Post
from .forms import PostForm
from django.shortcuts import redirect
from django.contrib.auth.models import User # new
from rest_framework import generics, permissions # new
from .serializers import PostSerializer, UserSerializer # new
# Create your views here.


def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'base/post_detail.html', {'post': post})


def post_new(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.published_date = timezone.now()
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm()
    return render(request, 'base/post_edit.html', {'form': form})

def post_edit(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == "POST":
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.published_date = timezone.now()
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'base/post_edit.html', {'form': form})



class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    def perform_create(self, serializer): # new
        serializer.save(author=self.request.user)

class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,) # new

class UserList(generics.ListAPIView): # new
    queryset = User.objects.all()
    serializer_class = UserSerializer

class UserDetail(generics.RetrieveAPIView): # new
    queryset = User.objects.all()
    serializer_class = UserSerializer

Note on the view above I've used ListCreateAPIView to create a read-only endpoint that lists all available Post instances and then RetrieveUpdateDestroyAPIView for a detail view of individual posts which supports CRUD-like functionality.

nano base/urls.py

from django.urls import path 
from . import views
from rest_framework.urlpatterns import format_suffix_patterns # new
urlpatterns = [
    path('post//', views.post_detail, name='post_detail'),
    path('post//edit/', views.post_edit, name='post_edit'),
    path('post/new/', views.post_new, name='post_new'),
    path('posts/', views.PostList.as_view()), # new
    path('posts//', views.PostDetail.as_view()), # new
    path('users/', views.UserList.as_view()), # new
    path('users//', views.UserDetail.as_view()), # new
]
urlpatterns = format_suffix_patterns(urlpatterns) # new

Adding login to the browsable API
We can add login to the API page so that we don't have to go to the main site when we want to login,
to add a login view to the browsable API we need to edit the URLconf in our project-level bio_metric/urls.py file.

Add rest_framework.urls to the route.

nano bio_metric/urls.py

from django.contrib import admin
from django.urls import path, include
from django.views.generic.base import TemplateView 

urlpatterns = [
    path('base/', include('base.urls')),
    path('accounts/', include('accounts.urls')), 
    path('login/', include('rest_framework.urls')), # new
    path('', TemplateView.as_view(template_name='home.html'), name='home'), 
    path('accounts/', include('django.contrib.auth.urls')), 
    path('admin/', admin.site.urls),
]
Notice the name "login" above could be anything The important part is that "rest_framework.urls" .

Fire up the server and test CRUD functionality

python3 manage.py runserver 192.168.0.245:8000

Post Lists ----- I get all the posts
Post detail ----- details of a particular post
API login
User have login 
Now you can delete the post
I have deleted one post only two post remaining
Updating the second post
Creating a new post

New post has been created


List of the current posts
 The JSON file


Again here the List of the current posts same info being returned by the JSON file above
We cant enumerate the deleted or non existing content by id
We can enumerate existing content by the id




Associating Posts with Users
if you want to enumerate users you will run into this  'User' object has no attribute 'posts'
Based on my requirement's this is enough. I can do the crud functionality on the Post if you want to do the crud functionally to the user's as well you will need to do some modification on the serializers.py and views.py you may olso want to consider securing the API probably with https. Very unfortunate those topic are out of scope for this post.  

Popular posts from this blog

How To Install the Anaconda Python Distribution on Debian 9 And Running a public notebook server

Anaconda Distribution is an open-source package manager, environment manager and distribution of Python and R programming languages. With a collection of 1,000+ open source packages with free community support. Designed for data science and machine learning workflows, you can use it whether you are on Windows, macOS or Linux. The Anaconda distribution ships with the conda command-line package management utility. You can learn more about Anaconda and conda by reading the official Anaconda Documentation . Jupyter is a browser-based interpreter that allows you to interactively work with Python and R. Anaconda provides Jupyter as well. You can think of Jupyter as a digital notebook that gives you an ability to execute commands, take notes and draw charts.It’s primarily used by Data Scientists. But I find that very useful tool if you are learning Python or R. It’s basically the same as working on a shell but much better. The Jupyter notebook web application is based on a

django react app setting up the backend

On the previous article I demonstrated how we can use the generic views along with ModelSerializer classes to rapidly develop our REST APIs. Knowledge that you will need  in your career as full stack / backend developer, however think of this article as an extension to the previous one, equipped with what we already know about REST API we will step our game up and discuss about ViewSet, ModelViewset we will dig deep into the concepts of Routers which allow us to manage our api routes in a simple and sophisticated manner as well as helping to speed up building APIs even further. There for on part II of this article i'll work you through on how React application can consume this RESTful API. There for at the end of the day we will have a full stack web app, in short we strat our development at the backend then later on we move at the frontend... so are you excited and ready to take the challange? lets do this then..... you can get source code for the bakend on github Preparat