Hello friends, in this article we will be going over swagger UI implementation with function based views in django rest framework (drf).
I decided to write an article on this because recently I faced different problems when trying to add in basic functionalities of swagger UI into my API, I was using function based views instead of the more popular classed based view approach.
In this article we will be building a basic product API with CRUD applications, using function based views and then adding swagger UI to make our API more presentable.
Setting up Our Development Environment
We need to create our django project, but we need to setup our development environment.
Firstly create a working directory and in it, create a virtual environment, using:
mkdir django-swagger
cd django-swagger
virtualenv env
Next we need to activate our virtual environment, use the appropriate code that works for your OS, using any of the code below:
Windows --> env\Scripts\activate
Linux --> source env/bin/activate
Now we install our dependencies, using:
pip install django djangorestframework
Next, we start our django project and then create an app, using:
django-admin startproject main .
python manage.py startapp prodcuts
Creating Our Models
We have successfully created our development environment, now we need to create the model(s) we will work with. We only need a simple Product
model with a few fields. Add this to your models.py
file.
class Product(models.Model):
title = models.CharField(max_length=224)
description = models.CharField(max_length=255)
def __str__(self):
return self.title
Creating Our Serializers
Now, we run our makemigrations
command so as to populate our database with the Product
table.
Next we create our serializers.py
file in our product
, and add the following code.
from rest_framework import seriailizers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = "__all__"
class UpdateProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["title", "description"]
def update(self, instance, validated_data):
instance.title = validated_data.get("title", instance.title)
instance.description = validated_data.get("description", instance.description)
We now have our models and serializers ready. We now need to create CRUD
views in our views,py
file. Add this block of code to your views.py
file.
Create Product View
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
from .models import Product
from .serializers import ProductSerializer
@api_view(['POST'])
def create_product(request):
if request.method == 'POST':
create_product = ProductSerializer(data=request.data)
if create_product.is_valid():
title = create_product.validated_data.get('title')
description = create_product.validated_data.get('description')
product = Product.objects.create(title=title, description=description)
product.save()
return Response(create_product.data, status=status.HTTP_201_CREATED)
else:
return Response(create_product.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response({"Error": "Invalid request type"}, status=status.HTTP_400_BAD_REQUEST)
Read Product View
@api_view(['GET'])
def read_product(request):
if request.method == 'GET':
all_products = Product.objects.all()
serialized_products = ProductSerializer(all_products, many=True)
return Response(serialized_products.data, status=status.HTTP_200_OK)
else:
return Response(serialized_products.errors, status=status.HTTP_400_BAD_REQUEST)
Update Product View
@pi_view(['PATCH'])
def update_product(request, id):
if request.method == 'PATCH':
try:
product = Product.objects.get(id=id)
update_seriailizer = UpdateProductSerializer(product, data=request.data, partial=True)
if update_serializer.is_valid():
update_serializer.save()
return Response({"Message": "Product updated"}, status=status.HTTP_200_OK)
else:
return Response(update_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return Response({"Error": f"Unexpected error {e} occurred.", status=status.HTTP_400_BAD_REQUEST)
else:
return Response({"Error": "Invalid request type", status=status.HTTP_400_BAD_REQUEST})
Delete Product View
@api_view(['POST'])
def delete_view(request):
if request.method == 'POST':
all_products = Product.objects.all()
all_product.delete()
return Response({"Message": "All Products have been deleted"}, status=status.HTTP_200_OK)
else:
return Response({"Error": "Invalid request type"}, status=status.HTTP_400_BAD_REQUEST)
Product View Urls
Now, we need to create urls to help us access our endpoints. In our product
directory we need to create a urls.py
file and populate it with this code:
from django.urls import path
from . import views
urlpatterns = [
path('create', views.create_product, name='create-product'),
path('read', views.read_product, name='read-all-products'),
path('update/<int:id>', views.update_product, name='update-products'),
path('delete', views.delete_view, name='delete-product')
]
Next we need to add our url configuration to the root directory urls.py
file by adding this to the existing urlpatterns
list:
path('product/', include('product.urls'))
Also make sure to import include
alongside path
from django.urls
.
Swagger Implementation
We're done with building our products API, now, we will see how to use swagger UI with our API we have built. Firstly we need to install a package called drf_yasg
, this will help us with the swagger UI implementation. The yasg
stands for Yet Another Swagger Generator
.
To install it, let's open our terminal and enter this command:
pip install drf_yasg
After the installation add it to your installed apps in your, like this:
INSTALLED_APPS = [
'drf_yasg',
]
We have added the package to our installed apps, now to see the magic happen we need to add a few routes to our root urls.py
file.
Add this block of code to your root urls.py
file, right below the django.urls
import:
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="Products API",
default_version='v1',
description="Description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="<your-gmail>@gmail.com"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
What this block of code does is to convert our generic django rest framework UI into swagger UI anytime we check our API in the browser. It will be much cleaner and easier to understand.
We need to add this block of code too, this should go into the urlpatterns
list:
path('', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
path('json/', schema_view.without_ui(cache_timeout=0), name='schema-json'),
This is what will help us view the swagger UI using routes.
Now if we run our server we should see this:
What we have done up to this point is to convert our drf UI into swagger UI in the browser, we haven't really implemented.
If you check you can only see the endpoints our API has, to allow our API send POST
and UPDATE
requests we need to configure that too.
Here is an example:
We should be able to send POST
requests for our create-product
endpoint but we can't, we can see that there are no fields for us to add our data for out POST
request, but we can change that.
We need to add this import to our views.py
file:
from drf_yasg.utils import swagger_auto_schema
swagger_auto_schema
is a decorator that helps us add the requests functionalities we need to our function based views. It takes several parameters, but the ones we're interested in are method
and request_body
.
method
specifies the type of request that is being sent to our endpoint.
request_body
specifies the serializer that the endpoint is going to use to send different requests, mainly used with POST
, PUT
, PATCH
, and DELETE
methods.
To our Create Product View
we need to add this block of code above our api_view
decorator.
@swagger_auto_schema(method='POST', request_body=ProductSerializer)
After adding this, head on to your browser and see the effect of the change you've made, you should see this now:
Now we can see that there is a required data object
with the fields we specified in our serializer class which we will use to send a POST
request. To send a request, click on the Try it out
button and fill in the data object (if necessary) and hit Execute
.
We now need to do the same thing we did to our Create Product View
to our other views. Adding the swagger_auto_schema
decorator above the api_view
decorator of each view with their corresponding methods and request_body serializer.
Read Product View
@swagger_auto_schema(method='GET')
Update Product View
@swagger_auto_schema(method='PATCH', request_body=UpdateProductSerializer)
Delete Product View
@swagger_auto_schema(method='POST', request_body=ProductSerializer)
Now run your server and see the new changes take effect.
With this you can now implement swagger UI into drf using function based views.
If you find this article interesting and informative, kindly leave a like and share, and if you still have any questions, leave them in the comment section below.
Cheers!!
Top comments (1)
@swagger_auto_schema(method='POST', request_body=ProductSerializer)
in this code,
Instead of mentioning serializer, can we add single parameters in request body?