REST APIs can be created elegantly using python’s djangorestframework module in a Django project.
Django Rest Framework module provides simple and sophisticated ways of creating APIs. Django Rest Framework is
also known as DRF, we will use this acronym to save space and time.
Following prerequisites required
1. Install “djangorestframework” module in your django project.
`pip install djangorestframework`
2. Add “rest_framework” to INSTALLED_APPS list in settings.py
3. Run the migrations
`python manage.py makemigrations`
`python manage.py migrate`
Now setup is completed, lets create simple API which returns sample data in JSON format.
We will go from simple to complex, We will create simple API which gives you JSON output, just a message “Hello
World”. Once completed, you check output in your browser at http://localhost:8000/hello-world-api/
Fastest (but not dirty) way
The shortest and fastest way to is to create a python function and write all logic there.
In views.py file add following
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework.decorators import api_view | |
from rest_framework.response import Response | |
@api_view() | |
def hello_world_api(request): | |
return Response({"message": "Hello, world!"}) |
Add link to urls.py file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib import admin | |
from django.urls import path | |
from drf_api.views import hello_world_api | |
urlpatterns = [ | |
path('hello-world-api/', hello_world_api), # Add this to add API to project urls | |
path('admin/', admin.site.urls), | |
] |
Somewhat more elegant way.
We can create python class which extends APIView.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework.views import APIView | |
from rest_framework.response import Response | |
class HelloWorldAPIView(APIView): | |
def get(self, request): | |
return Response({'message': 'Hello, World!'}) |
To use above as API you will need to connect it to URL to do that,
in project’s `urls.py` file add following entry in `urlpatterns` list.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib import admin | |
from django.urls import path | |
from drf_api.views import HelloWorldAPIView | |
urlpatterns = [ | |
path('hello-world-api/', HelloWorldAPIView.as_view()), | |
path('admin/', admin.site.urls), | |
] |
Specialized Views, don’t get stuck into “Hello World” only
There are classes in DRF which you can extend to create APIs for specialized cases, like listing database
table entries, creating/modifying/deleting database table record. You can do things close to zen of
REST APIs.
You can create REST API around Django model which is database table under the hood. Almost in all cases model corresponds to table in database. Here you can think of this model as “resource” in REST terminology, and on this resource you can create apis to honor REST verbs like GET, POST, PUT, DELETE etc.
DRF provides lot of model based views which gives you built in APIs to manage model. To use model based APIs you will need model
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
class Movie(models.Model): | |
title = models.CharField(max_length=100) | |
director = models.CharField(max_length=100) |
and model serializer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework import serializers | |
from .models import Movie | |
class MovieSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Movie | |
fields = '__all__' |
Lets start with ListAPIView, Classes you can extend are
ListAPIView
This gives you GET API to list model items.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework.generics import ListAPIView | |
from .models import Movie | |
from .serializers import MovieSerializer | |
class MovieListAPIView(ListAPIView): | |
queryset = Movie.objects.all() | |
serializer_class = MovieSerializer |
You will need to add URL entry in project URLs,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.contrib import admin | |
from django.urls import path | |
from drf_api.movie_list_api_view import MovieListAPIView | |
urlpatterns = [ | |
path('movie-list/', MovieListAPIView.as_view()), | |
path('admin/', admin.site.urls), | |
] |
You can check http://localhost:8000/movie-list/, Response for API will be,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"id": 1, | |
"title": "The Shawshank Redemption", | |
"director": "Frank Darabont" | |
}, | |
{ | |
"id": 2, | |
"title": "The Godfather", | |
"director": "Francis Ford Coppola" | |
}, | |
{ | |
"id": 3, | |
"title": "The Dark Knight", | |
"director": "Christopher Nolan" | |
} | |
] |
We will not list code samples for rest of the views here, but I have added them on github project
CreateAPIView
This POST api to create model object. e.g. curl -d “{\”title\”: \”New Movie 2\”, \”director\”:\”New Director 2\”}” -H “Content-Type: application/json”
-X POST http://localhost:8000/movie-create-api/ This will create new model objects. It supports POST method only.
RetrieveAPIView
This gives you GET API(accepts pk of model as param) to give particular model item. e.g. http://localhost:8000/movie-retrieve-api/1/ , This will fetch and display movie record of id equal to 1. If you give invalid id, API will return empty response with 404 status.
DestroyAPIView
This gives you DELETE API(accepts pk of model as param) to delete particular model item, e.g. curl -X DELETE http://localhost:8000/movie-destroy-api/3/, This will delete movie with id 3. It accepts DELETE as http request method.
UpdateAPIView
This gives you PUT API (accepts pk of model as param) to update particular model item. It accepts PUT and PATCH as http request methods. For PUT http method it needs you send whole object to update.
e.g. curl -d “{\”title\”: \”The Shawshank Redemption 2\”, \”director\”:\”Frank Darabont I\”}” -H “Content-Type:
application/json” -X PUT http://localhost:8000/movie-update-api/1/
For PATCH http method you can send individual attribute.
e.g. curl -d “{\”title\”: \”The Shawshank Redemption 3\”}” -H “Content-Type: application/json” -X PATCH http://l
ocalhost:8000/movie-update-api/1/
RetrieveUpdateAPIView
This is mix of retrieve and update. GET request along with id will retrieve the model record curl -H “Content-Type: application/json” -X GET http://localhost:8000/movie-retrieve-destroy-api/1/ and PUT and PATCH requests will update the same model record.
ListCreateAPIView
This is mix of list and create. GET request to API will return list of model items curl -H “Content-Type: application/json” -X GET http://localhost:8000/movie-list-create-api/, and POST request to API will try to create new model item curl -d “{\”title\”: \”New Movie\”, \”director\”:\”New Director\”}” -H “Content-Type: application/json” -X
POST http://localhost:8000/movie-list-create-api/
.
RetrieveDestroyAPIView
This is mix of retrieve and delete. GET request to API along with id will retrieve model record curl -H “Content-Type: application/json” -X GET http://localhost:8000/movie-retrieve-destroy-api/4/
and DELETE request will delete the corresponding model record curl -H “Content-Type: application/json” -X DELETE http://localhost:8000/movie-retrieve-destroy-api/4/.
RetrieveUpdateDestroyAPIView
This is mix of retrieve, update and delete. GET request curl -H “Content-Type: application/json” -X GET http://localhost:8000/movie-retrieve-update-destroy-api/5/ will return model with given id. PUT,PATCH will update model object as whole and partially respectively. DELETE request will delete the model object of given id.
Viewsets
You can group related views in single class aka viewsets in DRF.
ViewSet
This class by default does not provide any actions, but you can define list, create, retrieve, update, partial_update and destroy methods to use it in REST convention on URL with methods GET, POST, GET, PUT, PATCH and DELETE respectively.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework.viewsets import ViewSet, ModelViewSet | |
from .models import Movie | |
from .serializers import MovieSerializer | |
from rest_framework.response import Response | |
from django.shortcuts import get_object_or_404 | |
class MovieViewSet(ViewSet): | |
def list(self, request): | |
queryset = Movie.objects.all() | |
serializer = MovieSerializer(queryset, many=True) | |
return Response(serializer.data) | |
def create(self, request): | |
serializer = MovieSerializer(data=request.data) | |
serializer.is_valid(raise_exception=True) | |
serializer.save() | |
return Response(serializer.data) | |
def retrieve(self, request, pk=None): | |
queryset = Movie.objects.all() | |
movie = get_object_or_404(queryset, pk=pk) | |
serializer = MovieSerializer(movie) | |
return Response(serializer.data) | |
def update(self, request, pk=None): | |
queryset = Movie.objects.all() | |
movie = get_object_or_404(queryset, pk=pk) | |
serializer = MovieSerializer(movie, data=request.data) | |
serializer.is_valid(raise_exception=True) | |
serializer.save() | |
return Response(serializer.data) | |
def partial_update(self, request, pk=None): | |
queryset = Movie.objects.all() | |
movie = get_object_or_404(queryset, pk=pk) | |
serializer = MovieSerializer(movie, data=request.data, partial=True) | |
serializer.is_valid(raise_exception=True) | |
serializer.save() | |
return Response(serializer.data) | |
def destroy(self, request, pk=None): | |
queryset = Movie.objects.all() | |
movie = get_object_or_404(queryset, pk=pk) | |
movie.delete() | |
return Response({}) |
In project urls file you can add link as following,
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework.routers import DefaultRouter | |
from drf_api.movie_viewset_api import MovieViewSet | |
router = DefaultRouter() | |
router.register(r'movies-viewset', MovieViewSet, basename='movies-viewset') | |
urlpatterns = router.urls |
GenericViewSet
This gives you class where get_object and get_queryset methods are given, it does not provide any action by default If you want to use it you will need to extend one of the mixins, like CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin and DestroyModelMixin these will give you API to create, list, retrieve, update and delete model entries respectively. Using GenericViewSet and mixins you can have actions which suits your need.
ModelViewSet
This is mix of list, create, retrieve, update, partial update, delete views for model.
ReadOnlyModelViewSet
This is mix of list, retrieve actions for model. It does not involve any action which will update model entry, hence good for read only API endpoint for your model.
As there are lot of options available, this may be confusing sometime which one to pick. May be this can help to choose,
Do you have a single model class which can be referred as “resource” in REST way
Yes – Use specialized model based views i.e. ModelViewSet, ReadOnlyModelViewSet.
No – Use APIView and customized it to your need.
If you have multiple model class manipulations or using raw queries, but still want to create API in REST way, Use ViewSet and implement the REST methods required.
If you have use case where model is not involved or it is not core of the functionality e.g. You are uploading file processing it and returning result on the fly, you can use APIView.
What next?
Apart from view functions, classes there are serializers, permission classes you will need to know to make
maximum use of DRF toolset.
I hope this will help you to create APIs(REST) for your application. It is very simple and reliable
way, once you grok it, you will need to look back to create APIs in Django. This blog lists general
cases, there can be other specialized or complex cases where you might need to do more, but basics will
remain same. You can start your API with this basic knowledge and then extend it further to match the
requirement. If you want to know more about how particular API case can be addressed you can add comment here, I will try to get back to you.
Till then, happy coding 🙂