DEV Community

Cover image for Swagger Implementation using function based views drf
Somtochukwu
Somtochukwu

Posted on

Swagger Implementation using function based views drf

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Now we install our dependencies, using:

pip install django djangorestframework
Enter fullscreen mode Exit fullscreen mode

Next, we start our django project and then create an app, using:

django-admin startproject main .
python manage.py startapp prodcuts
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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})
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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')
] 
Enter fullscreen mode Exit fullscreen mode

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'))
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

After the installation add it to your installed apps in your, like this:

INSTALLED_APPS = [
   'drf_yasg',
]
Enter fullscreen mode Exit fullscreen mode

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],
)
Enter fullscreen mode Exit fullscreen mode

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'),
Enter fullscreen mode Exit fullscreen mode

This is what will help us view the swagger UI using routes.

Now if we run our server we should see this:

Swagger Image 1

Swagger Image 2

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:

Swagger Image 3

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

After adding this, head on to your browser and see the effect of the change you've made, you should see this now:

Swagger Image 5

Swagger Image 6

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')
Enter fullscreen mode Exit fullscreen mode

Update Product View

@swagger_auto_schema(method='PATCH', request_body=UpdateProductSerializer)
Enter fullscreen mode Exit fullscreen mode

Delete Product View

@swagger_auto_schema(method='POST', request_body=ProductSerializer)
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
karthiksalankimutt profile image
karthiksalankimutt ks

@swagger_auto_schema(method='POST', request_body=ProductSerializer)
in this code,
Instead of mentioning serializer, can we add single parameters in request body?