DjangoDjango Rest FrameworkDRF Spectacular

    How to Add Swagger for Django with Django Rest Framework using DRF Spectacular | Part 1

    How to Add Swagger in Django and DRF Using drf-spectacular Prerequisites Basic understanding of Django Basic understanding of Django REST Framework (DRF) Introduction In this artic...

    Dec 9, 2025
    5 min read
    360 views
    How to Add Swagger for Django with Django Rest Framework using DRF Spectacular | Part 1

    How to Add Swagger in Django and DRF Using drf-spectacular

    Prerequisites

    • Basic understanding of Django
    • Basic understanding of Django REST Framework (DRF)

    Introduction

    In this article, we're going to see how to add Swagger (OpenAPI) documentation to our Django REST Framework APIs using drf-spectacular.

    Step 1: Create Project Directory

    First, create a folder for your project:

    bash
    mkdir django-swagger-integration
    cd django-swagger-integration

    Initialize uv (a Python package manager):

    bash
    uv init

    Step 2: Install Required Libraries

    Add the necessary dependencies:

    bash
    uv add django djangorestframework drf-spectacular

    Step 3: Create Django Project

    Generate your Django project:

    bash
    uv run django-admin startproject django_swagger_integration .

    This will create the basic project structure.

    Step 4: Configure Settings

    We need to tell Django about the installed packages. Open django_swagger_integration/settings.py and add the following to INSTALLED_APPS:

    python
    INSTALLED_APPS = [
        # ... other apps
        'rest_framework',
        'drf_spectacular',
    ]

    Step 5: Create APIs App

    Create a new app called apis:

    bash
    uv run python manage.py startapp apis

    Don't forget to add 'apis' to INSTALLED_APPS in settings.py.

    Step 6: Configure DRF Spectacular

    We need to configure Django to use drf-spectacular's AutoSchema. Add this to settings.py:

    python
    REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
    }

    If your REST_FRAMEWORK dictionary already has additional settings, keep those and just add this as an additional key-value pair.

    Next, add the Spectacular settings:

    python
    SPECTACULAR_SETTINGS = {
        'TITLE': 'Your Project API',
        'DESCRIPTION': 'Your project description',
        'VERSION': '1.0.0',
        'SERVE_INCLUDE_SCHEMA': False,
        # OTHER SETTINGS
    }

    Step 7: Add Schema Endpoint

    In django_swagger_integration/urls.py, add the schema generation endpoint:

    python
    from django.contrib import admin
    from django.urls import path, include
    from drf_spectacular.views import SpectacularAPIView
    
    urlpatterns = [
        path("admin/", admin.site.urls),
        path('api/', include('apis.urls')),
        path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
    ]

    The api/schema/ endpoint generates and returns your API schema in OpenAPI format (JSON or YAML).

    Step 8: Add Swagger UI

    Now that the schema generation is set up, we need to add the Swagger UI to visualize it. Import the Swagger view in your urls.py:

    python
    from drf_spectacular.views import SpectacularSwaggerView
    
    urlpatterns = [
        path("admin/", admin.site.urls),
        path('api/', include('apis.urls')),
        path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
        path('api/schema/swagger/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
    ]

    Our basic setup is now complete!

    Step 9: Create API Views

    Let's add some sample API views to demonstrate the Swagger documentation.

    Create Models

    In apis/models.py:

    python
    from django.db import models
    from django.contrib.auth.models import User
    
    class Product(models.Model):
        name = models.CharField(max_length=255)
        price = models.DecimalField(max_digits=10, decimal_places=2)
        description = models.TextField()
        stock = models.IntegerField()
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
        created_by = models.ForeignKey(User, on_delete=models.CASCADE)
        updated_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='updated_products')
    
        def __str__(self):
            return self.name
        
        def decrease_stock(self, quantity):
            if quantity > self.stock:
                raise ValueError("Insufficient stock")
            self.stock -= quantity
            self.save()
    
    class Order(models.Model):
        product = models.ForeignKey(Product, on_delete=models.CASCADE)
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        quantity = models.IntegerField()
        total_price = models.DecimalField(max_digits=10, decimal_places=2)
        created_at = models.DateTimeField(auto_now_add=True)
    
        def __str__(self):
            return f"Order {self.id} - {self.product.name}"

    Create Serializers

    Create apis/serializers.py:

    python
    from .models import Product, Order
    from django.contrib.auth.models import User
    from rest_framework import serializers
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = User
            fields = ['id', 'username', 'email']
    
    class ProductAddUpdateSerializer(serializers.ModelSerializer):
        class Meta:
            model = Product
            exclude = ['created_at', 'updated_at', 'created_by', 'updated_by']
        
        def create(self, validated_data):
            request = self.context.get('request')
            if request and hasattr(request, "user") and request.user.is_authenticated:
                validated_data['created_by'] = request.user
                validated_data['updated_by'] = request.user
            return super().create(validated_data)
    
        def update(self, instance, validated_data):
            request = self.context.get('request')
            if request and hasattr(request, "user") and request.user.is_authenticated:
                validated_data['updated_by'] = request.user
            return super().update(instance, validated_data) 
        
    
    class ProductListSerializer(serializers.ModelSerializer):
        class Meta:
            model = Product
            fields = '__all__'
    
    
    class OrderAddSerializer(serializers.ModelSerializer):
        class Meta:
            model = Order
            exclude = ['created_at', 'user']
        
        def create(self, validated_data):
            request = self.context.get('request')
            if request and hasattr(request, "user") and request.user.is_authenticated:
                validated_data['user'] = request.user
            return super().create(validated_data)
    
    
    class OrderListSerializer(serializers.ModelSerializer):
        product = ProductListSerializer()
        user = UserSerializer() 
        class Meta:
            model = Order
            fields = '__all__'

    Create Views

    In apis/views.py:

    python
    from rest_framework import generics
    from .models import Product, Order
    from .serializers import ProductListSerializer, ProductAddUpdateSerializer, OrderAddSerializer, OrderListSerializer
    from rest_framework.permissions import IsAuthenticated
    
    class ProductListCreateView(generics.ListCreateAPIView):
        queryset = Product.objects.all()
        permission_classes = [IsAuthenticated]
    
        def get_serializer_class(self):
            if self.request.method == 'POST':
                return ProductAddUpdateSerializer
            return ProductListSerializer
    
    class ProductRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
        queryset = Product.objects.all()
        permission_classes = [IsAuthenticated]
    
        def get_serializer_class(self):
            if self.request.method in ['PUT', 'PATCH']:
                return ProductAddUpdateSerializer
            return ProductListSerializer
    
    class OrderCreateListView(generics.CreateAPIView):
        permission_classes = [IsAuthenticated]
        def get_serializer_class(self):
            if self.request.method == 'POST':
                return OrderAddSerializer
            return OrderListSerializer
        
        def get_queryset(self):
            if self.request.method == 'GET':
                return Order.objects.filter(user=self.request.user)
            return Order.objects.all()
        
    class OrderDetailView(generics.RetrieveAPIView):
        serializer_class = OrderListSerializer
        permission_classes = [IsAuthenticated]
        def get_queryset(self):
            return Order.objects.filter(user=self.request.user)
    

    Create URLs

    Create apis/urls.py:

    python
    from django.urls import path
    from .views import ProductListCreateView, ProductRetrieveUpdateDestroyView, OrderCreateView
    
    urlpatterns = [
        path('products/', ProductListCreateView.as_view(), name='product-list-create'),
        path('products/<int:pk>/', ProductRetrieveUpdateDestroyView.as_view(), name='product-retrieve-update-destroy'),
        path('orders/', OrderCreateView.as_view(), name='order-create'),
    ]

    Step 10: Run Migrations and Test

    Run the migrations:

    bash
    uv run python manage.py makemigrations
    uv run python manage.py migrate

    Start the development server:

    bash
    uv run python manage.py runserver

    Now visit http://127.0.0.1:8000/api/schema/swagger/ to see your Swagger UI documentation!

    Conclusion

    You've successfully integrated Swagger documentation into your Django REST Framework project using drf-spectacular. The Swagger UI provides an interactive interface to explore and test your API endpoints, making it easier for developers to understand and use your API.

    J
    Written by

    Jobi S S

    Portfolio

    admin

    Sharing technical insights, engineering concepts, and practical modern software development guides.

    Community Discussion

    Enjoyed this read? Show your support or share your thoughts.

    Comments (0)

    No comments yet. Be the first to comment!

    📬 Enjoyed this article?

    Get new posts on Django, FastAPI, and system design straight to your inbox. No spam — unsubscribe whenever you want.