How to create a CRUD API in Django Rest Framework with APIView, ViewSets, and Swagger Documentation
Tags: django, python, djangorestframework, swagger
Published: 2023-08-06 18:04:45

Are you looking to develop a robust CRUD (Create, Read, Update, Delete) API in Django Rest Framework? In this blog post, we'll guide you through the process of building a CRUD API using both APIView and ViewSets, and we'll enhance it with Swagger documentation for easy API exploration and testing.

Django Rest Framework (DRF) is a powerful toolkit for building Web APIs in Django. It provides a flexible and easy-to-use framework for creating RESTful APIs with features like authentication, serialization, and request/response handling. We'll utilize two different approaches offered by DRF to build our CRUD API.

APIViews:

APIViews in DRF allow us to define the API logic as class-based views. We'll create an APIView-based class that handles the different HTTP methods (GET, POST, PUT, DELETE) to perform the respective CRUD operations. With the help of the swagger_auto_schema decorator, we'll also add detailed Swagger annotations for request and response descriptions.

```
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from .models import MyModel
from .serializers import MyModelSerializer

class MyModelAPIView(APIView):
    @swagger_auto_schema(
        operation_description="Get all models",
        responses={200: MyModelSerializer(many=True)}
    )
    def get(self, request):
        queryset = MyModel.objects.all()
        serializer = MyModelSerializer(queryset, many=True)
        return Response(serializer.data)

    @swagger_auto_schema(
        operation_description="Create a new model",
        request_body=MyModelSerializer,
        responses={201: MyModelSerializer()}
    )
    def post(self, request):
        serializer = MyModelSerializer(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)

    @swagger_auto_schema(
        operation_description="Update a model",
        request_body=MyModelSerializer,
        responses={200: MyModelSerializer()}
    )
    def put(self, request, pk):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        serializer = MyModelSerializer(instance, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @swagger_auto_schema(
        operation_description="Delete a model"
    )
    def delete(self, request, pk):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
```

ViewSets:

ViewSets in DRF provide a higher-level abstraction for building APIs. We'll create a ViewSet-based class that encapsulates the logic for handling different actions like list, create, retrieve, update, and delete. By registering the ViewSet with a router, we'll automatically generate the URL patterns for our API. Similar to the APIView approach, we'll use the swagger_auto_schema decorator to add Swagger annotations for clear API documentation.

```
from rest_framework import status
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from .models import MyModel
from .serializers import MyModelSerializer

class MyModelViewSet(ViewSet):
    @swagger_auto_schema(
        operation_description="Get all models",
        responses={200: MyModelSerializer(many=True)}
    )
    def list(self, request):
        queryset = MyModel.objects.all()
        serializer = MyModelSerializer(queryset, many=True)
        return Response(serializer.data)

    @swagger_auto_schema(
        operation_description="Create a new model",
        request_body=MyModelSerializer,
        responses={201: MyModelSerializer()}
    )
    def create(self, request):
        serializer = MyModelSerializer(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)

    @swagger_auto_schema(
        operation_description="Get a model by ID",
        responses={200: MyModelSerializer()}
    )
    def retrieve(self, request, pk=None):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        serializer = MyModelSerializer(instance)
        return Response(serializer.data)

    @swagger_auto_schema(
        operation_description="Update a model",
        request_body=MyModelSerializer,
        responses={200: MyModelSerializer()}
    )
    def update(self, request, pk=None):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        serializer = MyModelSerializer(instance, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    @swagger_auto_schema(
        operation_description="Delete a model"
    )
    def destroy(self, request, pk=None):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        instance.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    @swagger_auto_schema(
        operation_description="Custom action",
        responses={200: "Custom action executed"}
    )
    @action(detail=True, methods=['post'])
    def custom_action(self, request, pk=None):
        try:
            instance = MyModel.objects.get(pk=pk)
        except MyModel.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)

        # Perform custom action here

        return Response("Custom action executed", status=status.HTTP_200_OK)
```

Swagger Documentation:

Swagger is an open-source toolset for designing, building, documenting, and consuming RESTful APIs. By incorporating Swagger into our DRF project, we can generate interactive API documentation that allows developers to explore and test the API endpoints effortlessly. We'll use the drf-yasg package, which integrates Swagger with DRF, to automatically generate the API documentation based on the Swagger annotations we added.

To install drf-yasg, run the following command:

```
pip install drf-yasg
```

Once installed, you can configure Swagger in your project's urls.py file as follows:

```
from django.urls import path, include
from rest_framework import routers
from .views import MyModelViewSet, MyModelAPIView
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

router = routers.DefaultRouter()
router.register(r'models', MyModelViewSet)

schema_view = get_schema_view(
    openapi.Info(
        title="Your API",
        default_version='v1',
        description="API documentation powered by Swagger",
        terms_of_service="https://your-terms-of-service-url.com/",
        contact=openapi.Contact(email="contact@your-api.com"),
        license=openapi.License(name="Your API License"),
    ),
    public=True,
)

urlpatterns = [
    path('', include(router.urls)),
    path('api-docs/', schema_view.with_ui('swagger', cache_timeout=0), name='api-docs'),
    # Other paths in your URL configuration
]
```

In this tutorial, we've covered the basics of building a CRUD API using both APIView and ViewSets in Django Rest Framework. We've also learned how to enhance the API documentation using Swagger and the drf-yasg package. By following this guide, you can create a powerful and well-documented API that adheres to RESTful principles and provides a seamless experience for developers consuming your API.

Stay tuned for more Django Rest Framework tutorials and explore the limitless possibilities of building robust and scalable APIs with Django!

Shivam Rohilla | Python Developer
Thank You