Building a REST API with Django REST Framework
Tags: javascript, webdev, react, django
Published: 2021-11-12 09:05:47

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](https://dev.to/faruqt/how-to-connect-django-to-reactjs-1a71). 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
```javascript
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;
```javascript
 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 👇
```javascript
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`
```javascript
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.
```javascript
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.
```javascript
fields = '__all__'
```
Your core directory should look like this now.
![Core app directory](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r6swvf83flahm3uptwho.png)
####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.
```javascript
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.
```javascript
@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.
```javascript
@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 👇.
```javascript
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.
```javascript
from core.views import front, note, note_detail
```
and then map them to various `urls`.
```javascript
    path("notes/", note, name="note"),
    path("notes/<int:pk>/", note_detail, name="detail"),
```
Your urls.py file should look like this 👇
```javascript
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.
```javascript
python manage.py makemigrations 
```
```javascript
python manage.py migrate 
```
and then we run the application with
```javascript
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qgoneh7vif0lpixa8tx2.png)
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n0jgjh155o4orvsrr0ua.png)
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](https://www.django-rest-framework.org/api-guide/viewsets/)

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.
```javascript
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](https://www.linkedin.com/in/faruq-abdulsalam) or [Twitter](https://twitter.com/Ace_II) and I'll ensure I respond as quickly as I can. Ciao 👋