Building a REST API with Django REST Framework

Welcome to part 2 of this series. Here we'll be creating the REST API for a Notes application and also finishing up the backend development.

If you come across this part first, you can check out part 1 here. We already handled the project setup in that tutorial.

We'll continue from where we stopped in part 1; so this would be easy to follow as well.

Let's get started!

Creating Api endpoints

You should still be in the project1 directory. If not, navigate to the directory

cd React-Django/project1

Please ensure you are in the folder with the manage.py file.

Django REST framework is a powerful and flexible toolkit for building Web APIs. We are going to use this to create the API endpoints. First, we have to install it;

pip install djangorestframework

settings.py

Next, we register it under the installed apps section in the settings.py file. The INSTALLED_APPS section in your settings.py file should look like this 👇

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #my_apps
    'core',
    'rest_framework',
]

models.py

Then we create the models in the models.py file inside the app directory core

class Notes(models.Model):
    title = models.CharField(max_length=60)
    content = models.CharField(max_length=120)

    def __str__(self):
        return self.title

serializers.py

We also need to create a new file serializers.py inside the app directory "core". This will contain the serializer that will be responsible for converting the model into data types understandable by javascript and the react framework.

from rest_framework import serializers
from .models import Notes

class NoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Notes
        fields = ('id', 'title', 'content')

Here we import the serializers class from the installed Django REST framework package and also the Notes model created earlier. Then we declare the fields of the model that we want to have their data converted. If you have a model with several fields and you want to serialize all; you can simply add the line below in place of the fields line above.

fields = '__all__'

Your core directory should look like this now.
Core app directory

views.py

Finally, to complete the backend setup, we need to update the views.py file. Add these import lines to the top of the file.

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .serializers import NoteSerializer
from .models import Notes

We'll make use of function-based views with the api_view decorator. This will allow us to declare the method type. By default only the GET method is allowed so we have to specify the POST and DELETE methods that we need.

Directly below the previous front view, we'll create the note view with the api_view decorator. This view will handle the GET and POST methods so we have to add it to the decorator as well.

@api_view(['GET', 'POST'])
def note(request):

    if request.method == 'GET':
        note = Notes.objects.all()
        serializer = NoteSerializer(note, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = NoteSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

What we are doing here is to check for the type of request method and if it is a get request method we query the database to retrieve all the available notes and then we serialize the data received from the database. The many=True flag is being used here because we are getting a list of objects from the database and not just an object instance. Then we return the serialized data to the frontend for display.

However, if the request method is a post method; we deserialize the data submitted from the front end and we check to see if all the data is valid with is_valid method before accessing and storing the data in the database. We then return a successful status message to the frontend without any data. You'll see the reason for this when we start working on the front end.

Next, we create the note_detail view. This view will handle the DELETE method so we will include it in the api_view decorator.

@api_view(['DELETE'])
def note_detail(request, pk):
    try:
        note = Notes.objects.get(pk=pk)
    except Notes.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'DELETE':
        note.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Here we are passing primary key field(pk) together with the request to the note_detail function as we need to add the primary key to the database query so we can get the particular note to be deleted. Once the note with the specified primary key has been retrieved successfully we check for the method type; if it is DELETE, that particular note is deleted from the database.

We have finished creating the views, if you followed all the steps above your views.py file should look like this 👇.

from django.shortcuts import render
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .serializers import NoteSerializer
from .models import Notes
# Create your views here.

def front(request):
    context = {
        }

    return render(request, "index.html", context)

@api_view(['GET', 'POST'])
def note(request):

    if request.method == 'GET':
        note = Notes.objects.all()
        serializer = NoteSerializer(note, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = NoteSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['DELETE'])
def note_detail(request, pk):
    try:
        note = Notes.objects.get(pk=pk)
    except Notes.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'DELETE':
        note.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

urls.py

We need to import the new view functions from the core app into the urls.py file. We will just add new lines to the existing ones.

from core.views import front, note, note_detail

and then map them to various urls.

path("notes/", note, name="note"),
    path("notes/<int:pk>/", note_detail, name="detail"),

Your urls.py file should look like this 👇

from django.contrib import admin
from django.urls import path
from core.views import front, note, note_detail

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", front, name="front"),
    path("notes/", note, name="note"),
    path("notes/<int:pk>/", note_detail, name="detail"),
]

We have finished up the API and backend setup. To test this, we migrate the changes we made to the models.py file into the database schema.

python manage.py makemigrations
python manage.py migrate

and then we run the application with

python manage.py runserver

You should still see the default react page, change your URL to http://127.0.0.1:8000/notes/. You'll see the Django REST framework browsable API.
drf browsable api
Paste the JSON data below in the content box and click on the POST button. This will be the format of the POST data that will be sent from the front end.

{
        "title": "Hello Ace!",
        "content": "This is a new note."
    }

Refresh the page and you'll see the new POST data. Voila! You have successfully executed the POST and GET methods.
POST and GET methods
Let's also test the DELETE method. Change your URL to http://127.0.0.1:8000/notes/1/. The digit at the end of the URL is the id of the note you want to delete. Click on the DELETE button and then return to the previous URL http://127.0.0.1:8000/notes/. You'll discover that the note created earlier has been deleted.

Note: Function-based views with api_view decorators were used for this tutorial for simplicity purposes. You can also choose to make use of viewsets which will handle all the various methods GET , POST , PUT , DELETE without you having to state them as we did with the api_view decorator. You can read more about viewsets here

We have confirmed that the API endpoints (GET, POST, DELETE) are working as desired so in part 3 we'll proceed to build the frontend where we'll be making the API calls using the API URLs above. I hope this was easy to follow as well. See you in part 3... Cheers!!!!

Bonus: To disable the Django REST framework browsable API view so users cant use it to interact with the database. Add this line to your settings.py file.

REST_FRAMEWORK = {
     'DEFAULT_RENDERER_CLASSES': (
         'rest_framework.renderers.JSONRenderer',
     )
 }

If you have any questions, feel free to drop them as a comment or send me a message on Linkedin or Twitter and I'll ensure I respond as quickly as I can. Ciao đź‘‹

18