Introduction
An API schema provides a standard definition of the details of your API, which can be rendered in interactive web pages, or can be used to generate client code.
In this post I describe how to modify a Django Rest Framework (DRF) application to serve an existing OpenAPI (aka Swagger) schema definition rendered using Swagger UI:
A sample application is available here.
Why not use a framework?
DRF does support generation of schemas using OpenAPI, however support for OpenAPI was only added in release 3.9, and it is still in its early stages. It's worth checking DRF for ongoing OpenAPI support because this is being actively developed.
If you are working with Swagger (OpenAPI v2), then you can potentially use drf-yasg to generate and serve a schema view. drf-yasg is a third-party plugin for DRF.
However, if you are using OpenAPI v3 (OpenAPI is the new name for Swagger), or have a pre-existing schema definition file, then using a third-party framework may be impossible or impractical, and the next best thing might be to render a static schema file, as I describe here.
There are other options in DRF for rendering a schema which don't involve OpenAPI (see the DRF) docs), although DRF is moving towards supporting OpenAPI.
What do we need to do?
To serve an existing API schema file from DRF, we need to do the following:
- Define the schema definition file.
- Configure DRF to serve the schema file.
- Install Swagger UI as a static resource.
- Create a Django Template to serve the UI.
These steps are described in detail below.
Install the DRF Tutorial
I have used a fork of the DRF tutorial to demonstrate the steps required.
You can clone the repo and run locally to see how it fits together.
The fork includes a schema.yaml
file for the tutorial project.
You can install the clone as follows:
git clone git@github.com:matthewhegarty/rest-framework-tutorial.git
cd rest-framework-tutorial
# Create a virtualenv to isolate our package dependencies locally
virtualenv env
source env/bin/activate
pip install -r requirements.txt
export DJANGO_SETTINGS_MODULE=tutorial.settings
python manage.py migrate
# when prompted, create a suitable password
python manage.py createsuperuser --email user@example.com --username admin
Now you can run the server with:
python manage.py runserver
Test the API
If the tutorial is installed correctly, then you should be able to browse http://localhost:8000/ and see the default API Root.
You can now login as Admin using the link at the top right hand side of the page, and log in using the credentials you created earlier.
Define the schema
First of all we need to serve the Schema definition. OpenAPI definitions can be written in either yaml or json. In this example, yaml is used.
As of DRF 3.9, you can generate a schema with:
python manage.py generateschema > schema.yml
However, the output of generateschema
is only going to be a stub of your API, and you will likely need to use this as a starting point, and add the specifics of your API.
As you work on your schema, you can validate it by copying the source definition into the Swagger editor tool.
I have already created a schema for the rest-framework-tutorial application
here.
Serve the schema
Once the schema is ready, it should be checked into source control. It can now be served as part of the application.
Create a 'static' directory under the application root, in which we will put static web content to serve to clients. For example:
mkdir -p snippets/static/openapi
Move your schema.yaml
into this new directory.
Update URLs
Now edit the URLs file (tutorial/urls.py
), and add the following:
from django.conf.urls.static import static
from tutorial import settings
urlpatterns = [
# Leave the existing urls defined here
] + static(settings.STATIC_URL)
This is making use of Django's static file serving functionality, and you should read Django's documentation on this topic.
Restart the server if necessary, and now hitting the endpoint should download the schema file, for example:
wget http://localhost:8000/static/openapi/schema.yaml
Install Swagger UI
The next step is to install the Swagger UI distribution into our static files, so that it can be served alongside the application.
Clone the Swagger UI repo locally.
Create static directory for Swagger UI
Create another directory under your static
root to serve the SwaggerUI files:
mkdir -p snippets/static/openapi/swagger-dist-ui
Now copy the contents of SwaggerUI's dist directory into the swagger-dist-ui
directory you just created, for example:
cp -av ../swagger-ui/dist/* snippets/static/openapi/swagger-dist-ui
Create a Django Template for Swagger UI
Our final step is to configure Django to serve Swagger UI. To do this we need to create a template which Django can serve SwaggerUI files from.
Create a new directory for the template:
mkdir -p snippets/templates
Now move the SwaggerUI index.html into this directory:
mv snippets/static/openapi/swagger-dist-ui/index.html snippets/templates/
Define Template Directory in Config
After the above step, check that your config references the templates directory correctly. In tutorial/settings.py
, add the 'templates' directory to DIRS
(leave all other config as is):
tutorial/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['templates'],
...
},
]
Configure Template
Now we need to edit the index.html
file so that it references static resources such as Swagger UI's js and css files.
Edit index.html
:
Add the
{% load static %}
directive to the top of the file.Work through the file and modify all static file references to use a
Django template directive.
For example, on line 7, change the href
reference from ./swagger-ui.css
to {% static "openapi/swagger-dist-ui/swagger-ui.css" %}
.
Do this for all other file references in index.html
.
- Change the url reference in
SwaggerUIBundle
to reference your schema file:url: "{% static "openapi/schema.yaml" %}"
The end product should look something like this.
Add a URL reference
Last of all, we need to add a reference to the template in urls.py
in order to serve the index.html
file.
from django.views.generic import TemplateView
urlpatterns = [
url('openapi/', TemplateView.as_view(template_name="index.html")),
url(r'^', include(router.urls))
]
Test the UI
Now if we browse to http://localhost:8000/openapi/ we should see the Swagger UI rendered with our schema.yaml.
Conclusion
In this post I've covered rendering an existing schema file in Swagger UI and serving the UI as part of a Django Rest Framework project.
This approach works well in Development, but is not suitable for Production use. Refer to the Django documentation for further guidance on serving static files in Production.
Further support for OpenAPI / Swagger is planned for Django Rest Framework, so this process might be refined in future DRF releases. Follow the release notes for updates.
Top comments (9)
Thanks for this! I was just wanting to do this as I've been playing with a (so far) hand-coded JSON:API 1.0 OAS 3.0 schema for a demo Django REST Framework JSON API (DJA) app. So far, DRF/DJA aren't yet ready to auto-generate the schema but I wanted to get a feeling for what's possible by hand-coding it.
Hi Alan - thanks for the response. You might want to check out FastAPI if you haven't already seen it. It's an API framework which supports OAS by default.
Thanks for the pointer Matt. We're pretty opinionated about Django;-) We get a lot of other value besides the JSONAPI support....
Hi guys!
Just wanted to let you know that there is another alternative. github.com/tfranzel/drf-spectacular (disclaimer i'm the author).
It's an OpenAPI 3 alternative to drf-yasg with a lot of similar features and wider support than the native DRF implementation. Some of the features are automatic components, validation, easy modification via decorators, SwaggerUI, ReDoc.
Thank you so much for this!
Love this, thank you so much!
Hi,
Is there anything like, the swagger UI is seen only when we are running on debugging mode?
Thanks for your perfect tutorial.
Just one edit :
python manage.py generateschema > schema.yml
to
python manage.py generateschema > schema.yaml
(the file extension has to be yaml, not yml)
This is fantastico my friend ..
I'll try this ..
sultan.org
Some comments may only be visible to logged-in visitors. Sign in to view all comments.