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.
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.
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.
Creating 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.
Creating Model
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.
HTTP Verb | Scope | Semantics | URL |
---|---|---|---|
GET | Transformer | Retrieve a single transformer | http://localhost:8000/transformers/{id}/ |
GET | Collection of Transformer | Retrieve all transformers in the collection | http://localhost:8000/transformers/ |
POST | Collection of Transformer | Create a new transformer in the collection | http://localhost:8000/transformers/ |
PUT | Transformer | Update an existing transformer | http://localhost:8000/transformers/{id}/ |
PATCH | Transformer | Partially update an existing transformer | http://localhost:8000/transformers/{id}/ |
DELETE | Transformer | Delete an existing transformer | http://localhost:8000/transformers/{id}/ |
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.
Creating Serializer
Create a new serializers.py Python file in your app (transformers) folder and add the below code:
Python3
from rest_framework import serializers from 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.
Function-based Views
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, JsonResponse from django.views.decorators.csrf import csrf_exempt from rest_framework.parsers import JSONParser from transformers.models import Transformer from transformers.serializers import TransformerSerializer @csrf_exempt 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 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_exempt def 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()
transformer_list()
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.
transformer_detail()
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 path from 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 admin from 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
@api_view
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_view from rest_framework.response import Response from rest_framework import status from transformers.models import Transformer from 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.
<!–
–>
Please Login to comment…