Swagger with Django made easy | A drf-spectacular explainer
The problem
Let’s talk about the problem: We have an API that we want to document. We can maintain a document with that information or an HTML website, or even have it generated by some tool from our code. The downside to this approach is that we need to keep it maintained manually while it is human-readable, it’s not really machine-readable. This problem is solved by Swagger following OpenAPI guidelines.
Open API
Let’s start with understanding the Open API term first. Simply put, It is a format/specification that you have to follow when describing your APIs. It allows you to describe available endpoints, input, and output to the APIs, authentication methods, contact information, etc. In order to have robust and thorough documentation, you have to have this kind of information. And swagger helps us with that.
Swagger
It's a tool that helps you design, document, and run APIs then and there, just like postman(another tool for testing out APIs). It is based on of course latest Open API specifications. There are various tools that come with Swagger. We will focus on the tool that helps us test our APIs in our own browser. It is known as Swagger UI. More on this later.
Various tools for Django
So the task at hand is to create beautiful docs for our Django application according to the latest OpenAPI Specification(that is version 3).
We have various open-source packages:
a) django-rest-swagger — It is an inactive project for the last 3 years so we will skip that.
b) drf-yasg — It comes with a warning “drf-yasg is unlikely to soon if ever, get support for OpenAPI 3.0” so we will skip that also.
c) drf-spectacular — Now finally something latest and we can use!
Installation and validation that everything is working dandy and fine
mkdir swag
cd swag
We will be working in a directory called swag(That’s right!)
>swagenv\Scripts\activate
Activate your environment. I am using windows.
pip install django
pip install djangorestframework
pip install your frameworks in your environment.
django-admin startproject blog_project
cd blog_project
python manage.py migrate
python manage.py startapp posts
In this step, we have started a project called “blog_project”. Now we have our manage.py, initial url_conf, and settings.py. Now cd into our project and run the migrate command. It will make initial tables for our sessions, user, and group usage. Let’s start an app called “posts” and begin writing our code in it.
Now make serializers.py in your posts app and copy this
from django.contrib.auth.models import User, Group
from rest_framework import serializersclass UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['url', 'username', 'email', 'groups']class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ['url', 'name']
In views.py copy and paste this
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from rest_framework import permissions
from posts.serializers import UserSerializer, GroupSerializerclass UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]class GroupViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Group.objects.all()
serializer_class = GroupSerializer
permission_classes = [permissions.IsAuthenticated]
Change your URL conf like this in blog_project
from django.urls import include, path
from rest_framework import routers
from posts import viewsrouter = routers.DefaultRouter()
router.register(r"users", views.UserViewSet)
router.register(r"groups", views.GroupViewSet)urlpatterns = [
path("", include(router.urls)),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
]
In blog_project/settings.py add a variable REST_FRAMEWORK which will be useful for later. Add pagination while you are at it. Pagination helps you when the objects to be shown are pretty big in numbers. Not related to this demo though.
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
Add “rest_framework” to the INSTALLED_APPS variable in settings only.
INSTALLED_APPS = ['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles','rest_framework','posts',]
Now run the server!
python manage.py runserver
The server should be up and running on http://127.0.0.1:8000/
Without logged in view should be like this
Logged in view
Now let’s start integrating swagger.
drf-spectacular
Install it in your environment like everything else
pip install drf-spectacular
Add “drf_spectacular” in INSTALLED_APPS in settings.py
add auto schema to REST_FRAMEWORK in settings.py again
REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',}
Open up your urls.py(URL conf) and make these changes, as these URLs are required for swagger UI
from django.urls import include, path
from rest_framework import routers
from posts import views
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
router = routers.DefaultRouter()
router.register(r"users", views.UserViewSet)
router.register(r"groups", views.GroupViewSet)
urlpatterns = [
path("", include(router.urls)),
path('admin/', admin.site.urls),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
path("schema/", SpectacularAPIView.as_view(), name="schema"),
path(
"docs/",
SpectacularSwaggerView.as_view(
template_name="swagger-ui.html", url_name="schema"
),
name="swagger-ui",
),
]
Update:- I have been informed by @tfranzel himself, the author of this repository, that drf-spectacular has gone through some changes. Rather than creating the custom “swagger-ui.html”, you can use the default one without any hitch. So please skip this portion of the tutorial. Please gander at the comment section if you need any further clarification. I will delete this section in a few days' time.
Now as you might have noticed we required “swagger-ui.html”. I assume you should be at the level of manage.py. Make a new directory named “templates” there and in that a new file named “swagger-ui.html”.
Then open “swagger-ui.html” and paste the following code
<!DOCTYPE html>
<html>
<head>
<title>Swagger</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3/swagger-ui.css">
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js"></script>
<script>
const ui = SwaggerUIBundle({
url: "{% url 'schema' %}",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
layout: "BaseLayout",
requestInterceptor: (request) => {
request.headers['X-CSRFToken'] = "{{ csrf_token }}"
return request;
}
})
</script>
</body>
</html>
In settings.py make the following changes in the TEMPLATES dictionary DIRS key
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
If all goes well. Go to http://127.0.0.1:8000/docs/ and you will see your APIs swagger look
To run it remember to first authorize yourself. Swagger provides you with various options for that. There is an authorization button on this page. Click it and you will get a pop-up something like this:
Authorize yourself, you can use your superuser credentials for this demo
Go to GET point of /users/ and click on execute
you will get a response something like this. Now you can use this instead of the postman. It is easy to use and in your browser only. Play and experiment with it yourself.
Your Swagger UI documentation implemented in the OpenAPI 3.0 standard is ready. This concludes our blog. I hope you have learned something.
For other farmeworks
You can implement swagger in Flask also. There are quality resources on it. I will link one such resource here. This uses Flask and Flask-RESTPlus for the integration of swagger.