How To Implement Swagger In Django Rest Framework (drf)

INTRODUCTION:

Swagger is an open-source framework that helps developers design, build, document, and consume RESTFUL web services. It is used to document APIs, which helps the developers to understand the basic behavior of every endpoint created. It also outlines all available models in a particular project. This is very helpful to developers as it saves long hours of meetings.

Swagger uses JavaScript Object Notation (JSON) and YAML Ain't Markup Language (YAML) called openAPI specification__ (OAS) to define APIs. This specification includes details such as endpoints, methods, requests, and responses.Some of the importance of integrating swagger docs in the backend of your code includes:

  • Automated documentation of API
  • Testing and debugging

This article will focus on swagger documenting UI and integrating swagger in your Django REST framework project.

PREREQUISITE

  • Python

SET UP A VIRTUAL ENVIRONMENT

Create a virtual environment. Depending on the version of Python you downloaded. For python 3.12. 1. Use this command.

python -m venv {venv name}

Where the ‘{venv name}’ refers to any desired name you give to your virtual environment(venv). The convention is using Venv but whatever name you choose to use is totally up to you. It should look like this

python -m venv venv

Run this command on Windows to activate your virtual environment

venv/Scripts/activate

On Linux, run this command to activate your virtual environment

venv/bin/activate

Install Django within your virtual environment using this command

pip install django

Install Django REST framework(DRF)

pip install djangorestframework

These are the necessary packages you need to start up a simple django Restframework (DRF) application

CREATE A SAMPLE APPLICATION

To test swagger interactive User Interface(UI), you need to create a sample Create, Read, UPDATE, Delete (CRUD) application using Django rest framework. Run the command below to set up a new project.

django-admin startproject myproject

This will create a new directory called myproject with the basic structure of Django in it. You can run your server to make sure everything is working fine.

Navigate into your project directory

  cd myproject

create a sample product app to handle basic CRUD operations

python manage.py startapp products

This will create a new directory with the basic structure of an app in it.

Open the settings.py file and include products to your INSTALLED_APPS.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products',                     # sample App 
    'rest_framework',               # Django rest framework(DRF)
]

Close and exit the settings.py file.

Navigate to your app directory

cd products

Open the models.py and create a sample model database schema

class Product(models.Model):
    id = models.UUIDField(primary_key=True)
    name = models.CharField(max_length=300)
    description = models.CharField(max_length=500)
    quantity = models.BigIntegerField()
    createdat = models.DateTimeField(auto_now=True)
    updatedat = models.DateTimeField(auto_now_add=True)

Save and exit the models.py file

Run migrations with the code below

python manage.py makemigrations

Apply those changes by running

python manage.py migrate

This will execute and apply all changes made to the database schema

Create a serializer.py file in your products app directory and create a serializer class to serialize your data.

from rest_framework import serializers
from .models import Product


class ProductSerializer(serializers.Serializer):
      class Meta:
        model = Product
        fields = '__all__'

Save and exit the serializer.py file.

In your views.py, create a the logic for the sample crud application.

from django.shortcuts import get_object_or_404
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from accounts.models import Product
from .serializers import ProductSerializer

class ProductListCreateAPIView(APIView):
    """
    API endpoint for listing and creating products.

    GET:
    Returns a list of all products.

    POST:
    Creates a new product.

    Raises:
    HTTP_500_INTERNAL_SERVER_ERROR: If an internal server error occurs.
    """
    def get(self, request, format=None):
        """
        Retrieves a list of all products.

        Args:
        request: HTTP request object.
        format: The requested data format. Default is None.

        Returns:
        Response containing a list of serialized products.
        """
        try:
            products = Product.objects.all()
            serializer = ProductSerializer(products, many=True)
            return Response(serializer.data)
        except Exception as e:
            return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def post(self, request, format=None):
        """
        Creates a new product.

        Args:
        request: HTTP request object.
        format: The requested data format. Default is None.

        Returns:
        Response containing information about the created product or validation errors.
        """
        try:
            serializer = ProductSerializer(data=request.data)
            if serializer.is_valid():
                serializer.save()
                response = {
                    "message": "Product added successfully",
                    "status_code": 201,
                    "data" : serializer.data
                }
                return Response(response, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            response= {
                "message": "internal server error",
                "status_code": 500,
                "data": {'detail': str(e)},
            }
            return Response(response, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ProductDetailAPIViews(APIView):
    """
    API endpoint for retrieving, updating, and deleting individual products.

    GET:
    Retrieves details of a specific product.

    PUT:
    Updates details of a specific product.

    DELETE:
    Deletes a specific product.

    Raises:
    HTTP_404_NOT_FOUND: If the requested product does not exist.
    HTTP_500_INTERNAL_SERVER_ERROR: If an internal server error occurs.
    """
    def get_object(self, pk):
        """
        Helper function to retrieve a product object by its primary key (pk).

        Args:
        pk: The primary key of the product.

        Returns:
        Product object.

        Raises:
        HTTP_404_NOT_FOUND: If the product with the given primary key does not exist.
        """
        try:
            return Product.objects.get(pk=pk)
        except Product.DoesNotExist:
            return get_object_or_404(Product, pk=pk)


    def get(self, request, pk, format=None):
        """
        Retrieves details of a specific product.

        Args:
        request: HTTP request object.
        pk: The primary key of the product.
        format: The requested data format. Default is None.

        Returns:
        Response containing details of the specified product.
        """
        product = self.get_object(pk)
        serializer = ProductSerializer(product)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        """
        Updates details of a specific product.

        Args:
        request: HTTP request object.
        pk: The primary key of the product.
        format: The requested data format. Default is None.

        Returns:
        Response containing updated details of the product or validation errors.
        """
        product = self.get_object(pk)
        serializer = ProductSerializer(product, data=request.data)
        if serializer.is_valid():
            serializer.save()
            response = {
                "message": "Product successfully updated",
                "status_code": 200,
                "data": serializer.data
            }
            return Response(response, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


    def delete(self, request, pk):
        """
        Deletes a specific product.

        Args:
        request: HTTP request object.
        pk: The primary key of the product.

        Returns:
        Response confirming the deletion of the product.
        """
        try:
            product = get_object_or_404(Product, pk=pk)
            product.delete()
            return Response({'message': 'Product deleted successfully.'})
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=500)

Save and exit

In urls.py file create the URLs for product.

from django.urls import path
from .views import ProductListCreateAPIView, ProductDetailAPIViews

urlpatterns = [
    # URL pattern for listing all products and creating new products
    path('', ProductListCreateAPIView.as_view(), name='product-list-create'),

    # URL pattern for retrieving, updating, and deleting a specific product
    path('<uuid:pk>/', ProductDetailAPIViews.as_view(), name='product-detail'),

    # URL pattern for updating a specific product
    path('<uuid:pk>/update/', ProductDetailAPIViews.as_view(), name='product-update'),

    # URL pattern for deleting a specific product
    path('<uuid:pk>/delete/', ProductDetailAPIViews.as_view(), name='product-delete'),
]

SET UP SWAGGER

There are certain configuration you need to enable to use swagger in DRF. These include installing packages like django-rest-swagger which allow you interact swagger in DRF and drf-yasg a library that automates documentation for Django REST Framework APIs

Install restframework swagger with the command below

pip install django-rest-swagger

install Django REST Framework - Yet Another Swagger Generator (drf-yasg)

pip install drf-yasg

Navigate to the myproject directory

cd ..

Include the libraries in your INSTALLED_APPS in the settings.py file

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'products',                     # sample App 
    'rest_framework_swagger',       # REST Framework Swagger 
    'rest_framework',               # Django rest framework(DRF)
    'drf_yasg'                      # Yet Another Swagger generator(yasg)
]

Save and exit

Navigate to your myproject urls.py file, and do the basic settings for Swagger docs with the code block below

from rest_framework_swagger.views import get_swagger_view
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
from rest_framework import permissions


schema_view = get_schema_view(
    openapi.Info(
        title="myproject",
        default_version='v1',),
    public=True,
    permission_classes=(permissions.AllowAny,),
)

urlpatterns = [

       path('products/', include('products.urls')),
       path('api/docs/', schema_view.with_ui('swagger', cache_timeout=0),name='schema-swagger-ui'),

].

Save and exit

Run your django server

python manage.py runserver

Output:

Swagger Documentation

Congratulations, you have successfully implemented Swagger.

SET UP SWAGGER FOR DEPLOYMENT

Note For deployment purposes, please configure your static files with the steps below: WhiteNoise is a Python library that helps you serve static files in your Django application. WhiteNoise integrates seamlessly with Django to provide efficient and easy-to-configure static file handling.

Install whiteNoise

Install whiteNoise with this command

pip install whitenoise

Update MIDDLEWARE

Update your Django settings to use WhiteNoise for serving static files by adding whiteNoise to your middleware in the settings.py file.

MIDDLEWARE = [
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]

Configure staticfiles

Ensure that your staticfile is configured correctly in the settings.py file with the code below

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/

STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

COLLECT STATIC FILES

Run collectstatic management command to gather all static files into the STATIC_ROOT directory

Python manage.py collectstatic

Now, you are ready for deployment!

Conclusion

This article provides a step by step guide on how to add swagger documentation to Django projects using drf-yasg and django-rest-framework. It covers installing the necessary packages and configurations

In conclusion, Django REST framework Swagger is a powerful tool that allows developers to easily document their APIs and provide documentation to other developers who may be using or consuming these APIs. For more detailed information about Django and its features, refer to the official documentation here: https://docs.djangoproject.com

Look into installing setuptools

8
Subscribe to my newsletter

Read articles from mamuro oyiboluawhore directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

mamuro oyiboluawhore
mamuro oyiboluawhore