How I Created a REST API Using Django REST Framework – Part 2

5/5 - (1 vote)

As a continuation of the first part of this project tutorial, we will add CRUD functionality to the REST API. CRUD is an acronym for create, read, update and delete. We want to give our users the option to create, read, update and delete Nigerian foods.

A corresponding HTTP request method is used to perform such actions. We use POST to create content, GET to read content, PUT to update, and DELETE to delete content. We will use each of these methods while creating the view functions.

πŸ’‘ Part 1: How I Created a REST API Using Django REST Framework

Functional-Based views

Add the following to the views.py file to create functional-based views.

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Food
from .serializer import FoodSerializer


@api_view(['GET'])
def get_food(request):
    food = Food.objects.all()
    serializer = FoodSerializer(food, many=True)
    return Response(serializer.data)

At the top, we import api_view, a REST framework wrapper to write API views. The @api_view decorator is for functional-based views. The decorator takes a GET request method to query the database and retrieves all the data. It then serializes the data and returns a response object in JSON format.

Let’s map a URL to the views to see what we have done on a browser. First of all, go to food/urls.py file and make some adjustments to the code.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('nigerianFood.urls')),
]

Now that we have informed Django of the existence of app-level URLs, let’s create the file.

from django.urls import path
from .views import get_food
urlpatterns = [
    path('food/get/api', get_food, name='get_food'),
]

Start the local server and go http://127.0.0.1:8000/api/get/food to see something similar to the image below.

Let’s continue with the CRUD operation to allow users to create content using the POST request method.

@api_view(['POST'])
def post_food(request):
    serializer = FoodSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
    return Response(serializer.data)

Since we are expecting data from the user, no need to query the database. We make sure that the data entered by the user is valid before it can be saved.

Add this to nigerianFood/urls,py

from .views import post_food
path('food/post/api', post_food, name='post_food')

Start the local server and go to http://127.0.0.1:8000/api/post/food

Add another Nigerian food such as this:

{"name": "Edikaikong Soup",
 "description": "A nutritious Nigerian soup recipe made with fresh leafy vegetables, dried fish and assorted meat"
}

Go to the previous URL, you will see the data displayed. Make sure you follow the above pattern and use double quotes otherwise, it will result in an error.

Of course, we can combine the two functions into one like this:

@api_view(['GET', 'POST'])
def get_and_post_food(request):
    if request.method == 'GET':
        food = Food.objects.all()
        serializer = FoodSerializer(food, many=True)
        return Response(serializer.data)
    elif request.method == 'POST':
        serializer = FoodSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
        return Response(serializer.data)

And this will be the endpoint in the urls.py file

path('api/get-post/food', get_and_post_food, name='get_and_post_food'),

We can also do likewise for PUT and DELETE request methods. But for the sake of this tutorial, we have other ideas.

Class-Based Views

Let’s complete the CRUD application using class-based views.

from rest_framework.generics import UpdateAPIView, DestroyAPIView

class Update(UpdateAPIView):
    queryset = Food.objects.all()
    serializer_class = FoodSerializer

class Delete(DestroyAPIView):
    queryset = Food.objects.all()
    serializer_class = FoodSerializer

Class-based views adopt the principle of DRY thus, reducing the amount of code we need to write. The GenericAPIView class is used in class-based views. It is the parent class for all generic views including UpdateAPIView and DestroyAPIView.

Add these to your app-level URLs:

from .views import Update, Delete
urlpatterns = [
...
path('food/update/<int:pk>/', Update.as_view(), name='update'),
path('food/delete/<int:pk>/', Delete.as_view(), name='delete'),
]

Assuming you want to update food with id=1, simply go to this URLΒ  http://127.0.0.1:8000/food/update/1.

Try updating with this Nigerian food

{"name": "Beans and Ripe Plantains",
"description": "A tasty and popular Nigerian food cooked with beans and ripe plantains"
}

If you want to delete items, go to this URL, http://127.0.0.1:8000/food/delete/1/Β  assuming you want to delete item 1.

By implementing only class-based views, we have to create four classes to perform the CRUD operation. (also with CreateAPIView and ListAPIView).

Furthermore, we can still reduce our code to two classes and still perform the CRUD operation. Back to the views.py file.

from rest_framework.generics import, ListCreateAPIView, RetrieveUpdateDestroyAPIView
class FoodListCreate(ListCreateAPIView):
    queryset = Food.objects.all()
    serializer_class = FoodSerializer

class FoodUpdateDelete(RetrieveUpdateDestroyAPIView):
    queryset = Food.objects.all()
    serializer_class = FoodSerializer

Update your urls.py file to include every endpoint used in this tutorial. Of course, you can always check my GitHub page for the full snippets.

from django.urls import path
from .views import post_food, get_food, get_and_post_food, Update, Delete, FoodListCreate, FoodUpdateDelete


urlpatterns = [
    path('api/get-post/food', get_and_post_food, name='get_and_post_food'),
    path('api/post/food', post_food, name='post_food'),
    path('api/get/food', get_food, name='get_food'),
    path('food/update/<int:pk>/', Update.as_view(), name='update'),
    path('food/delete/<int:pk>/', Delete.as_view(), name='delete'),
    path('food/list-create/food/', FoodListCreate.as_view(), name='food_list_and_create'),
    path('food/update-delete/<int:pk>/', FoodUpdateDelete.as_view(), name='food_update_and _delete'),

]

Conclusion

This is how we come to the end of this project tutorial. We have created a Django REST framework that performs all the CRUD operations. Of course, there are other request methods but, the ones we learned are mostly used.

Notice we didn’t demonstrate how to use the APIView class. It makes it possible to refactor your code. You may want to check the documentation to learn more. The knowledge of Django REST API will help you going forward.

Alright, thanks for reading and have a nice day.