17
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!
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
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',
]
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
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__'
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)
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.
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.
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',
)
}
17