SwaggerUI inside Django Rest Framework
Tags: djangorestframework, openapi, swagger, django
Published: 2019-03-28 11:58:06

---
title: SwaggerUI inside Django Rest Framework
published: true
description: 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.
tags: #djangorestframework #openapi #swagger #django
---

### 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](https://swagger.io/tools/swagger-ui/):

![Swagger UI screenshot](https://thepracticaldev.s3.amazonaws.com/i/g8rvlt7uqaz48c2pad40.png)

A sample application is available [here](https://github.com/matthewhegarty/rest-framework-tutorial/tree/swagger-ui).

### Why not use a framework?

DRF does support [generation of schemas using OpenAPI](https://www.django-rest-framework.org/api-guide/schemas/), however support for OpenAPI was only added in [release 3.9](https://www.django-rest-framework.org/community/3.9-announcement/#built-in-openapi-schema-support),  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](https://github.com/axnsan12/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](https://www.django-rest-framework.org/api-guide/schemas/)) 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](https://github.com/matthewhegarty/rest-framework-tutorial/tree/swagger-ui) 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`](https://github.com/matthewhegarty/rest-framework-tutorial/blob/swagger-ui/snippets/static/openapi/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](https://thepracticaldev.s3.amazonaws.com/i/6ve4i0v9no0n3yhhg27c.png)

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](https://www.django-rest-framework.org/api-guide/schemas/#generating-a-schema-with-the-generateschema-management-command) 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](https://editor.swagger.io/).

I have already created a schema for the rest-framework-tutorial application
[here](https://github.com/matthewhegarty/rest-framework-tutorial/blob/swagger-ui/snippets/static/openapi/schema.yaml).

### 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](https://docs.djangoproject.com/en/dev/howto/static-files/) 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](https://github.com/swagger-api/swagger-ui) 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](https://github.com/swagger-api/swagger-ui/tree/master/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](https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html#L7), 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`.

3. Change the [url reference](https://github.com/swagger-api/swagger-ui/blob/master/dist/index.html#L42) in `SwaggerUIBundle` to reference your schema file: `url: "{% static "openapi/schema.yaml" %}"`

The end product should look something like [this](https://github.com/matthewhegarty/rest-framework-tutorial/blob/swagger-ui/snippets/templates/index.html).

#### 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](https://thepracticaldev.s3.amazonaws.com/i/g8rvlt7uqaz48c2pad40.png)

### 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](https://docs.djangoproject.com/en/2.1/howto/static-files/deployment/) 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](https://www.django-rest-framework.org/community/release-notes/) for updates.