Function based Views – Django Rest Framework

16 Feb, 2021
Django REST Framework allows us to work with regular Django views. It facilitates processing the HTTP requests and providing appropriate HTTP responses. In this section, you will understand how to implement Django views for the Restful Web service. We also make use of the @api_view decorator.
Before working on Django REST Framework serializers, you should make sure that you already installed Django and Django REST Framework in your virtual environment. You can check the below tutorials:
Create virtual environment using venv | Python
Django Installation
Django REST Framework Installation
Next, you can create a project named emt and an app named transformers. Refer to the following articles to check how to create a project and an app in Django.
How to Create a Basic Project using MVT in Django?
How to Create an App in Django?
In this section, we will be using PostgreSQL. You have to create a database named emt in PostgreSQL. You can check the below link for installation.
Install PostgreSQL on Windows
Note: if you need to work with SQLite, you can continue using the default database.
To make use of PostgreSQL, we need to install a Python-PostgreSQL Database Adapter (psycopg2). This package helps Django to interact with a PostgreSQL database. You can use the below command to install the psycopg2 package. Make sure the PostgreSQL bin folder is included in the PATH environmental variables, and the virtual environment is activated before executing the command.
pip install psycopg2
Now, you need to configure the PostgreSQL database in your Django project. By default, the database configuration has an SQLite database engine and database file name. You can check the setting.py Python file and replace the default database configuration with the PostgreSQL database configuration.
DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql',
       'NAME': 'emt',
       'USER': 'username',
       'PASSWORD': 'password',
       'HOST': '127.0.0.1',
       'PORT': '5432',
   }
}
Here, the name refers to the database name, the user and the password refers to the Postgres username and password.
At last, we need to install the httpie package in our virtual environment. We will be composing HTTPie commands for CRUD operation. You can activate the virtual environment and run the below command
pip install –upgrade httpie
Now, let’s create the Model and Serializer.
Before getting into class-based views let’s create a model and its serializer, which helps to demonstrate the working of function-based views.
In Django, Models are classes that deal with databases in an object-oriented way. Each model class refers to a database table and each attribute in the model class refers to a database column. Here, we will create a simple Transformer model in Django, which stores the transformer character name, alternate mode, description, and whether the character is alive or dead. We require the following attributes for our Transformer entity:
name
alternate_mode
description
alive
In a RESTFul web service, each resource is accessed using a unique URL, which means each employee has their unique URL. And, each method of the model is composed of an HTTP Verb and Scope.
Let’s get into the implementation of the Transformer model in Django. You can replace the models.py Python file with the below code:
Python3
from django.db import models  class Transformer(models.Model):    name = models.CharField(max_length=150, unique=True)    alternate_mode = models.CharField(        max_length=250,        blank=True,        null=True)    description = models.CharField(        max_length=500,        blank=True,        null=True)    alive = models.BooleanField(default=False)      class Meta:        ordering = ('name',)      def __str__(self):        return self.name
The Transformer model is a subclass of the django.db.models.Model class and defines the attributes and a Meta inner class.  It has an ordering attribute that orders the result in an ascending order based on the name. Let’s make the initial migrations using the below command (Make sure you defined your app (‘transformers.apps.TransformersConfig’) in INSTALLED_APPS in settings.py Python file)
python manage.py makemigrations
Next, apply all generated migrations using the below command:
python manage.py migrate
Applying generated migrations will create a Transformer table in the database.
Create a new serializers.py Python file in your app (transformers) folder and add the below code:
Python3
from rest_framework import serializersfrom transformers.models import Transformer  class TransformerSerializer(serializers.ModelSerializer):    class Meta:        model = Transformer        fields = "__all__"
Here, we created a TransformerSerializer class that inherits from the rest_framework.serializers.ModelSerializer. If you would like to dig deep into different types of serialized, you can check Django REST Framework (DRF) Serialization. Now, it’s time to begin our journey towards function-based Views. We will start with regular Django views and after then we will take advantage of the @api_view decorator.
Django views facilitate processing the HTTP requests and providing HTTP responses. On receiving an HTTP request, Django creates an HttpRequest instance, and it is passed as the first argument to the view function. This instance contains HTTP verbs such as GET, POST, PUT, PATCH, or DELETE.   The view function checks the value and executes the code based on the HTTP verb.  Here the code uses @csrf_exempt decorator to set a CSRF (Cross-Site Request Forgery) cookie. This makes it possible to POST to this view from clients that won’t have a CSRF token. Let’s get into the implementation process. You can add the below code in the views.py file.  
Python3
from django.http import HttpResponse, JsonResponsefrom django.views.decorators.csrf import csrf_exemptfrom rest_framework.parsers import JSONParser  from transformers.models import Transformerfrom transformers.serializers import TransformerSerializer  @csrf_exemptdef transformer_list(request):    """    List all transformers, or create a new transformer    """    if request.method == 'GET':        transformer = Transformer.objects.all()        serializer = TransformerSerializer(transformer, many=True)        return JsonResponse(serializer.data, safe=False)      elif request.method == 'POST':        data = JSONParser().parse(request)        serializer = TransformerSerializer(data=data)        if serializer.is_valid():            serializer.save()            return JsonResponse(serializer.data, status=201)        return JsonResponse(serializer.errors, status=400)  @csrf_exemptdef transformer_detail(request, pk):    try:        transformer = Transformer.objects.get(pk=pk)    except Transformer.DoesNotExist:        return HttpResponse(status=404)      if request.method == 'GET':        serializer = TransformerSerializer(transformer)        return JsonResponse(serializer.data)      elif request.method == 'PUT':        data = JSONParser().parse(request)        serializer = TransformerSerializer(transformer, data=data)          if serializer.is_valid():            serializer.save()            return JsonResponse(serializer.data)        return JsonResponse(serializer.errors, status=400)      elif request.method == 'DELETE':        transformer.delete()        return HttpResponse(status=204)
Let’s evaluate the code. Here we have two functions.
transformer_list()
transformer_detail()
The ransformer_list() function is capable of processing two HTTP verbs – GET and POST.  
If the verb is GET, the code retrieves all the transformer instances. 
    if request.method == 'GET':
        transformer = Transformer.objects.all()
        serializer = TransformerSerializer(transformer, many=True)
        return JsonResponse(serializer.data, safe=False)
It retrieves all the transformer’s data using the objects.all() method.
Serializes the tasks using TransformerSerializer.
Then the serialized data is rendered using JsonResponse() and returns the result
Note:  The many=True argument specified serializes multiple Transformer instances.
If the verb is POST, the code creates a new transformer. Here the data is provided in JSON format while composing the HTTP request.
    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = TransformerSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)
Uses JSONParser to parse the request,
Serialize the parsed data using TransformerSerializer,
If data is valid, it is saved in the database and returns the rendered result with the status code.
The transformer_detail() function is capable of processing three HTTP verbs – GET, PUT, and DELETE. If the verb is GET, then the code retrieves a single transformer instance based on the primary key. If the verb is PUT, the code updates the instance and save it to the database, and if it is DELETE, then the code deletes the instance from the database.
As a next step, we need to route URLs to views. You need to create a new Python file name urls.py in the app (transformers) folder and add the below code.
Python3
from django.urls import pathfrom transformers import views  urlpatterns = [    path('transformers/',         views.transformer_list,         name = 'employee-list'),    path('transformers/<int:pk>/',         views.transformer_detail,         name = 'employee-detail'),]
Now, let’s update the root URL configuration. You can add the below code to the urls.py file (the same folder that has the settings.py file).  
Python3
from django.contrib import adminfrom django.urls import path, include  urlpatterns = [    path('', include('transformers.urls')),]
Now it’s time to compose and send HTTP requests to test our views.
Let’s create a new entry. The HTTPie command is:
http POST :8000/transformers/ name=”Wheeljack” alternate_mode=”1977 Lancia Stratos Turbo” description=”Always inventing new weapons and gadgets” alive=”False”
Output
HTTP/1.1 201 Created
Content-Length: 152
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:23:10 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": false,
    "alternate_mode": "1977 Lancia Stratos Turbo",
    "description": "Always inventing new weapons and gadgets",
    "id": 7,
    "name": "Wheeljack"
}
Sharing the command prompt screenshot for your reference
Let’s update the alive field of the transformer (id=7) to True. The HTTPie command is:
http PUT :8000/transformers/7/ name=”Wheeljack” alternate_mode=”1977 Lancia Stratos Turbo” description=”Always inventing new weapons and gadgets” alive=”True”
Output
HTTP/1.1 200 OK
Content-Length: 151
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:26:22 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1977 Lancia Stratos Turbo",
    "description": "Always inventing new weapons and gadgets",
    "id": 7,
    "name": "Wheeljack"
}
Let’s retrieve all the transformers. The HTTPie command is:
http GET :8000/transformers/
Output
HTTP/1.1 200 OK
Allow: GET, POST, OPTIONS
Content-Length: 629
Content-Type: application/json
Date: Mon, 25 Jan 2021 05:43:36 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "alive": true,
        "alternate_mode": "1977 Lancia Stratos Turbo",
        "description": "Always inventing new weapons and gadgets",
        "id": 7,
        "name": "Wheeljack"
    },
    {
        "alive": true,
        "alternate_mode": "1979 Porsche 924",
        "description": "Eager and Daring",
        "id": 5,
        "name": "Cliffjumper"
    },
    {
        "alive": true,
        "alternate_mode": "1979 VW Beetle",
        "description": "Small, eager, and brave. Acts as a messenger, scout, and spy",
        "id": 3,
        "name": "Bumblebee"
    },
    {
        "alive": false,
        "alternate_mode": "1979 Freightliner Semi",
        "description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
        "id": 1,
        "name": "Optimus Prime"
    }
]
Sharing the command prompt screenshot
The @api_view is a decorator in the rest_framework.decorators module, and it is the base class for all the Django REST framework views. We can provide the allowed HTTP verbs as the http_methods_names argument (list of strings) in the @api_view decorator. If the RESTful Web service receives an unsupported HTTP Verb, the decorator returns an appropriate response rather than an unexpected error in Django.     
@api_view(http_method_names=['GET'])
You can replace the views.py file with the below code.
Python3
from rest_framework.decorators import api_viewfrom rest_framework.response import Responsefrom rest_framework import status  from transformers.models import Transformerfrom transformers.serializers import TransformerSerializer  @api_view(['GET','POST'])def transformer_list(request):    """    List all transformers, or create a new transformer    """    if request.method == 'GET':        transformer = Transformer.objects.all()        serializer = TransformerSerializer(transformer, many=True)        return Response(serializer.data)      elif request.method == 'POST':        serializer = TransformerSerializer(data=request.data)        if serializer.is_valid():            serializer.save()            return Response(serializer.data,                            status=status.HTTP_201_CREATED)        return Response(serializer.errors,                        status=status.HTTP_400_BAD_REQUEST)  @api_view(['GET','PUT','PATCH','DELETE'])def transformer_detail(request, pk):    try:        transformer = Transformer.objects.get(pk=pk)    except Transformer.DoesNotExist:        return Response(status=status.HTTP_404_NOT_FOUND)      if request.method == 'GET':        serializer = TransformerSerializer(transformer)        return Response(serializer.data)      elif request.method == 'PUT':        serializer = TransformerSerializer(transformer, data=request.data)          if serializer.is_valid():            serializer.save()            return Response(serializer.data)        return Response(serializer.errors,                        status=status.HTTP_400_BAD_REQUEST)    elif request.method == 'PATCH':        serializer = TransformerSerializer(transformer,                                           data=request.data,                                           partial=True)          if serializer.is_valid():            serializer.save()            return Response(serializer.data)        return Response(serializer.errors,                        status=status.HTTP_400_BAD_REQUEST)      elif request.method == 'DELETE':        transformer.delete()        return Response(status=status.HTTP_204_NO_CONTENT)
In the new code, the transformer_list and transformer_detail functions are wrapped with the @api_vew decorator. The transformer_list function supports GET and POST Verbs, whereas, transformer_detail method supports GET, PUT, PATCH, and DELETE verbs. These supported verbs are passed as decorator parameters for each method.  Here, we removed rest_framework.parsers.JSONParser class. This way we can let the code work with different parsers. We also replaced the JSONResponse class with a more generic rest_framework.response.Response class.
Let’s run the HTTPie command with OPTIONS verb to list the supported methods and verbs in the transformer_list method.
http OPTIONS :8000/transformers/
Output
HTTP/1.1 200 OK
Allow: GET, OPTIONS, POST
Content-Length: 225
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:52:10 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "description": "List all transformers, or create a new transformer",
    "name": "Transformer List",
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "renders": [
        "application/json",
        "text/html"
    ]
}
Sharing the command prompt screenshot
The output displays the details of the transformer_list method. It allows GET, OPTIONS, and POST HTTP Verbs. It also displays the different parses and renders that the function supports.
Let’s look at the transformer_detail function. The HTTPie command is
http OPTIONS :8000/transformers/1/
Output
HTTP/1.1 200 OK
Allow: OPTIONS, DELETE, GET, PATCH, PUT
Content-Length: 177
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:59:23 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "description": "",
    "name": "Transformer Detail",
    "parses": [
        "application/json",
        "application/x-www-form-urlencoded",
        "multipart/form-data"
    ],
    "renders": [
        "application/json",
        "text/html"
    ]
}
You can notice that the transformer_detail() function supports OPTIONS, DELETE, GET, PATCH, and PUT HTTP verbs. It also displays the different parses and renders that the function supports.
The @api_view decorator can parse different content types by choosing the appropriate parser. From the above output, we can notice the different parsers supported in the @api_view decorator. When we use the @api_view decorator, it automatically makes use of APIView class and its settings. This way we will be able to use the parsers and renders. 
The @api_view decorator helps the Django REST framework to examine the Content-Type header in the data attribute and identifies the exact parser to parse the request. It also invokes the rest_framework.negotiation.DefaultContentNegotiation class to select the suitable renderer for the request. 
Now, it’s time to compose and send different HTTP requests. Let’s create a new entry. The HTTPie command is
http POST :8000/transformers/ name=”Prowl” alternate_mode=”1979 Nissan 280ZX Police Car” description=”Strives to find reason and logic in everything” alive=”False”
Output
HTTP/1.1 201 Created
Allow: GET, POST, OPTIONS
Content-Length: 149
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:18:56 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": false,
    "alternate_mode": "1979 Nissan 280ZX Police Car",
    "description": "Strives to find reason and logic in everything",
    "id": 10,
    "name": "Prowl"
}
Sharing the command prompt screenshot 
Let’s update the alive field value to True. The HTTPie command is:
http PATCH :8000/transformers/10/ alive=”True”
Output
HTTP/1.1 200 OK
Allow: PATCH, PUT, DELETE, OPTIONS, GET
Content-Length: 148
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:22:35 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
    "alive": true,
    "alternate_mode": "1979 Nissan 280ZX Police Car",
    "description": "Strives to find reason and logic in everything",
    "id": 10,
    "name": "Prowl"
}
Sharing the command prompt screenshot
Let’s retrieve all the entries. The HTTPie command is:
http GET :8000/transformers/
Output
HTTP/1.1 200 OK
Allow: GET, POST, OPTIONS
Content-Length: 739
Content-Type: application/json
Date: Mon, 25 Jan 2021 16:23:52 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.7.5
Vary: Accept, Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

[
    {
        "alive": true,
        "alternate_mode": "1979 Nissan 280ZX Police Car",
        "description": "Strives to find reason and logic in everything",
        "id": 10,
        "name": "Prowl"
    },
    {
        "alive": true,
        "alternate_mode": "1977 Lancia Stratos Turbo",
        "description": "Always inventing new weapons and gadgets",
        "id": 7,
        "name": "Wheeljack"
    },
    {
        "alive": true,
        "alternate_mode": "1979 Porsche 924",
        "description": "Eager and Daring",
        "id": 5,
        "name": "Cliffjumper"
    },
    {
        "alive": true,
        "alternate_mode": "1979 VW Beetle",
        "description": "Small, eager, and brave. Acts as a messenger, scout, and spy",
        "id": 3,
        "name": "Bumblebee"
    },
    {
        "alive": false,
        "alternate_mode": "1979 Freightliner Semi",
        "description": "Optimus Prime is the strongest and most courageous and leader of all Autobots",
        "id": 1,
        "name": "Optimus Prime"
    }
]
Sharing the command prompt screenshot
In this section, we understood how to code Django views to process HTTP Request and returns HTTP Response. We also realized the importance of wrapping our views with the @api_view decorator. Finally, we composed and sent different requests including the OPTIONS HTTP request, which displays the supported HTTP verbs, parsers, and renderers.
Django-REST
Django-views
Python Django
Python
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
How to Install PIP on Windows ?
Python Classes and Objects
Python OOPs Concepts
Introduction To PYTHON
Python | os.path.join() method
How to drop one or multiple columns in Pandas Dataframe
How To Convert Python Dictionary To JSON?
Check if element exists in list in Python
Python | Get unique values from a list
Create a directory in Python