DEV Community

Matthew Hegarty
Matthew Hegarty

Posted on

SwaggerUI inside Django Rest Framework

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:

Swagger UI screenshot

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:

  1. Define the schema definition file.
  2. Configure DRF to serve the schema file.
  3. Install Swagger UI as a static resource.
  4. 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.

DRF 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:

  1. Add the {% load static %} directive to the top of the file.

  2. 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.

  1. 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.

Swagger UI screenshot

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.

Latest comments (9)

Collapse
 
arman7878 profile image
Arman7878 • Edited

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)

Collapse
 
tfranzel profile image
T. Franzel

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.

Collapse
 
shravanign profile image
Shravani GN

Hi,
Is there anything like, the swagger UI is seen only when we are running on debugging mode?

Collapse
 
saymy__name__ profile image
Jordan Engstrom

Love this, thank you so much!

Collapse
 
motazhejaze profile image
Motaz Hejaze

This is fantastico my friend ..
I'll try this ..

sultan.org

Collapse
 
geoberle profile image
Gerd Oberlechner

Thank you so much for this!

Collapse
 
n2ygk profile image
Alan Crosswell

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.

Collapse
 
matthewhegarty profile image
Matthew Hegarty

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.

Collapse
 
n2ygk profile image
Alan Crosswell

Thanks for the pointer Matt. We're pretty opinionated about Django;-) We get a lot of other value besides the JSONAPI support....