
Django Swagger: Add OpenAPI Docs with drf-spectacular
To add Swagger to a Django project in 2026, install drf-spectacular if you use Django REST Framework (DRF). It generates an OpenAPI 3 schema from your views and serializers and serves Swagger UI and ReDoc pages, in about ten lines of settings and URLs. DRF's own docs recommend it: the built-in OpenAPI generator is deprecated "in favor of third-party packages", with drf-spectacular named as the replacement. If you are not on DRF, Django Ninja gives you Swagger UI out of the box.
Swagger, OpenAPI, and the Django packages
"Swagger" is the old name of the specification; since version 3 it is called the OpenAPI Specification. Swagger UI is the interactive web page that renders an OpenAPI file and lets you click "Try it out" to send requests. A Django "swagger" setup therefore has two parts: something that generates the OpenAPI schema from your code, and a page that renders it.
| Package | Schema format | Status (October 2026) | Use it when |
|---|---|---|---|
| drf-spectacular | OpenAPI 3.0.3 / 3.1 / 3.2 | Actively maintained, 0.30.0 (July 2026), recommended by DRF | Any new or existing DRF project |
| drf-yasg | Swagger / OpenAPI 2.0 | Maintained (1.21.18, October 2026), no OpenAPI 3 planned | Legacy projects already built on it |
| django-rest-swagger | Swagger 2.0 | Abandoned years ago | Never; migrate off it |
| Django Ninja | OpenAPI, from type hints | Actively maintained, 1.7.1 | New APIs not built on DRF |
drf-yasg's own README says it is "unlikely to soon, if ever, get support for OpenAPI 3.0" and points new projects to drf-spectacular. OpenAPI 2.0 lacks things like oneOf, multiple servers, and proper request body components, and many code generators now expect 3.x. So drf-spectacular vs drf-yasg is not a close call for new code.
Set up Swagger UI in Django REST Framework
These steps follow the drf-spectacular README.
- Install it:
pip install drf-spectacular
- Register the app and the schema class in
settings.py:
INSTALLED_APPS = [
# ...
"rest_framework",
"drf_spectacular",
]
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
SPECTACULAR_SETTINGS = {
"TITLE": "Shop API",
"DESCRIPTION": "Products, carts and orders",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False, # hide the schema endpoint from the docs themselves
}
- Add the schema and UI routes to
urls.py:
from django.urls import path
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularRedocView,
SpectacularSwaggerView,
)
urlpatterns = [
# ... your API routes
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/docs/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
path("api/redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]
Run
python manage.py runserverand openhttp://127.0.0.1:8000/api/docs/. That is your Django Swagger URL. The raw schema is at/api/schema/(YAML by default; add?format=jsonfor JSON).Optionally export the schema to a file in CI and fail the build on warnings:
python manage.py spectacular --color --validate --file schema.yml
Committing schema.yml makes API changes visible in code review, and the file can feed client generators (openapi-generator, orval, openapi-typescript).
By default the Swagger UI and ReDoc assets load from a CDN. For air-gapped or strict-CSP environments, install drf-spectacular[sidecar], add drf_spectacular_sidecar to INSTALLED_APPS, and set "SWAGGER_UI_DIST": "SIDECAR", "SWAGGER_UI_FAVICON_HREF": "SIDECAR" and "REDOC_DIST": "SIDECAR" so the files are served by Django's static files.
A worked example: documenting a product endpoint
drf-spectacular reads ModelViewSets and GenericAPIViews with a serializer_class automatically. You only annotate what it cannot infer: query parameters you read by hand, custom responses, and plain APIViews.
# shop/serializers.py
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "name", "price", "in_stock"]
# shop/views.py
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from rest_framework import viewsets
from .models import Product
from .serializers import ProductSerializer
@extend_schema_view(
list=extend_schema(
summary="List products",
parameters=[
OpenApiParameter("in_stock", bool, description="Only products in stock"),
],
),
retrieve=extend_schema(summary="Get one product"),
)
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def get_queryset(self):
qs = super().get_queryset()
if self.request.query_params.get("in_stock") == "true":
qs = qs.filter(in_stock=True)
return qs
# urls.py
from rest_framework.routers import DefaultRouter
from shop.views import ProductViewSet
router = DefaultRouter()
router.register("products", ProductViewSet)
urlpatterns += router.urls
Reload /api/docs/: you get GET/POST /products/ and GET/PUT/PATCH/DELETE /products/{id}/, the Product schema with field types, the in_stock query parameter, and a working "Try it out" button.
For a plain APIView or @api_view function, declare the shapes yourself, otherwise drf-spectacular warns that it cannot guess the serializer:
from drf_spectacular.utils import extend_schema
from rest_framework.views import APIView
from rest_framework.response import Response
class StatsView(APIView):
@extend_schema(responses={200: StatsSerializer}, summary="Shop statistics")
def get(self, request):
return Response(StatsSerializer(compute_stats()).data)
Authentication and who can see the docs
- Authorize button: drf-spectacular reads your
DEFAULT_AUTHENTICATION_CLASSES(session, token, basic, and common JWT packages) and adds the matching security schemes, so Swagger UI shows an "Authorize" button. Add"SWAGGER_UI_SETTINGS": {"persistAuthorization": True}toSPECTACULAR_SETTINGSto keep the token across reloads. - Restrict access in production: by default anyone who can reach
/api/schema/can read your full API surface. Set"SERVE_PERMISSIONS": ["rest_framework.permissions.IsAdminUser"], or only add the docs routes whenDEBUGis true. Public APIs can leave them open on purpose.
Django Ninja: Swagger without DRF
If you are starting fresh and like FastAPI-style type hints, Django Ninja builds the schema from function signatures and serves Swagger UI at /api/docs with no extra package:
from ninja import NinjaAPI
api = NinjaAPI()
@api.get("/add")
def add(request, a: int, b: int):
return {"result": a + b}
# urls.py: path("api/", api.urls)
The trade-off: Ninja is not DRF, so DRF serializers, permissions, and third-party DRF packages don't plug in. For an existing DRF codebase, stay with drf-spectacular.
Common problems
- "could not resolve serializer" warnings: the view has no
serializer_classor overridesget_serializer_class()in a way the generator can't follow. Add@extend_schema(request=..., responses=...). - Blank Swagger page: usually a Content Security Policy blocking the CDN. Use the sidecar package.
- Migrating from drf-yasg: replace
swagger_auto_schemawithextend_schema(the argument names differ:responsesstays,manual_parametersbecomesparameterswithOpenApiParameter), remove theget_schema_viewroutes, and compare the old and new schemas before deleting drf-yasg.
Good generated docs still need human-written summaries and examples; see our REST API documentation best practices. For more tools in this space, browse API tools and documentation tools.
FAQ
Can I use Swagger with Django?
Yes. With Django REST Framework, install drf-spectacular and add SpectacularSwaggerView to your URLs; without DRF, Django Ninja serves Swagger UI automatically. Both generate an OpenAPI schema from your code, so the docs stay in sync with the endpoints.
What is the difference between Django REST Framework and Swagger?
DRF is the library you build the API with (serializers, views, auth). Swagger, now OpenAPI, is a format for describing an API, and Swagger UI is a page that renders that description. drf-spectacular is the bridge that turns a DRF API into an OpenAPI document.
Should I use drf-yasg or drf-spectacular?
drf-spectacular for anything new. It produces OpenAPI 3, is the option DRF's docs recommend, and drf-yasg says it does not plan OpenAPI 3 support. Keep drf-yasg only on a legacy project where migrating isn't worth it yet.
What is the Swagger URL in Django?
There is no fixed one; it is whatever path you map SpectacularSwaggerView to. Common choices are /api/docs/ or /api/schema/swagger-ui/ (the drf-spectacular README uses the latter). In Django Ninja it is /api/docs by default.
How do I generate a swagger.json or schema.yml file from Django?
Run python manage.py spectacular --file schema.yml (or --format openapi-json --file schema.json). Add --validate to check the output against the OpenAPI specification.