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:
mkdir django-swagger-integration
cd django-swagger-integrationInitialize uv (a Python package manager):
uv initStep 2: Install Required Libraries
Add the necessary dependencies:
uv add django djangorestframework drf-spectacularStep 3: Create Django Project
Generate your Django project:
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:
INSTALLED_APPS = [
# ... other apps
'rest_framework',
'drf_spectacular',
]Step 5: Create APIs App
Create a new app called apis:
uv run python manage.py startapp apisDon'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:
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:
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:
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:
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:
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:
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:
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:
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:
uv run python manage.py makemigrations
uv run python manage.py migrateStart the development server:
uv run python manage.py runserverNow 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.
Jobi S S
admin
Sharing technical insights, engineering concepts, and practical modern software development guides.
Community Discussion
Enjoyed this read? Show your support or share your thoughts.




