Django REST Framework

In this tutorial we’ll explore Django Rest Framework. This tutorial assumes that you are using a Linux-based system, macOS or WSL and you already have Python3 with venv installed in your system. So, let’s first create a new folder by entering following command:

mkdir myapp

Now, go inside this newly created folder:

cd myapp

Then create virtual environment:

python3 -m venv .env

And then mount the virtual environment:

source .env/bin/activate

If you are on Windows, then run following command:

.\.env\Scripts\activate

Now, install Django REST Framework:

pip install djangorestframework

The above command will also install Django along with Django Rest framework. Then create the project by entering following command:

django-admin startproject project .

Next, create an app named posts:

python manage.py startapp posts

Then, amend project/settings.py as follows in order to add posts app:

INSTALLED_APPS = [
    'posts',
    'rest_framework',
    # ...
]

Open posts/models.py and amend as follows:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    body = models.CharField(max_length=1000)
    def __str__(self):
        return self.title

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    content = models.CharField(max_length=1000)
    def __str__(self):
        return self.name

Now create migration by entering:

python manage.py makemigrations posts

Then execute:

python manage.py migrate

Create serializers.py file in the posts folder and then put following code in the posts/serializers.py:

from rest_framework import serializers
from .models import Post, Comment

class PostSerializer(serializers.HyperlinkedModelSerializer):
	class Meta:
		model = Post
		fields = ['title', 'body']

class CommentSerializer(serializers.HyperlinkedModelSerializer):
	class Meta:
		model = Comment
		fields = ['post', 'name', 'content']

Now open posts/views.py and amend it as follows:

from rest_framework import permissions, viewsets
from .serializers import PostSerializer, CommentSerializer
from .models import Post, Comment

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

class CommentViewSet(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
    permission_classes = [permissions.IsAuthenticated]

Next, open project/urls.py and amend it as follows:

from django.contrib import admin
from django.urls import include, path
from rest_framework import routers
from posts import views

router = routers.DefaultRouter()
router.register(r'posts', views.PostViewSet)
router.register(r'comments', views.CommentViewSet)

urlpatterns = [
	path('admin/', admin.site.urls),
	path('', include(router.urls)),
	path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

To register models in admin, open posts/admin.py and amend it as follows:

from django.contrib import admin
from .models import Post, Comment

admin.site.register(Post)
admin.site.register(Comment)

For pagination, open project/settings.py and insert following:

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 10,
}

Create superuser:

python manage.py createsuperuser --username admin --email admin@domain.com

Finally run server:

python manage.py runserver

And then head to Django admin:

http://localhost:8000/admin

Now add a couple of posts and then add a couple of comments to post 1. Now, you can test posts / comments api using command line tool curl by forming command like:

curl -u admin -H 'Accept: application/json; indent=4' http://127.0.0.1:8000/posts/

Or through browser:

http://localhost:8000/posts

BONUS CODE:

Now, if we want to make a nested api call like /posts/1/comments, we need to change posts/views.py as follows:

from rest_framework import permissions, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import PostSerializer, CommentSerializer
from .models import Post, Comment

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]
	
    @action(detail=True, methods=['get'])
    def comments(self, request, pk=None):
        post = self.get_object()
        comments = post.comment_set.all()
        
        serializer = CommentSerializer(
            comments, 
            many=True, 
            context={'request': request}
        )
        return Response(serializer.data)

class CommentViewSet(viewsets.ModelViewSet):
    queryset = Comment.objects.all()
    serializer_class = CommentSerializer
    permission_classes = [permissions.IsAuthenticated]

The above change introduces a custom action comments within PostViewSet class. Now you can enter the following URL:

http://localhost:8000/posts/1/comments

The above nested api call will return comments related to post 1 only.

Leave a Comment