Deploying your Django 3 App
Tags: python, django, devops, tutorial
Published: 2020-07-20 18:48:41

This article was originally published at: https://render.com/docs/deploy-django. It's recommended to read the linked version with improved syntax highlighting.

This guide will demonstrate how you can set up a local [Django](https://www.djangoproject.com/) development environment, create a simple view, and deploy it to [Render](https://render.com/). The application will be configured to use a Render PostgreSQL [database](https://render.com/docs/databases) and will use [Poetry](https://python-poetry.org/) to manage the Python virtual environment and project dependencies, though neither are requirements to deploy a Django project on Render.

The finished code for this example is available on [Github](https://github.com/render-examples/django) and the sample app can be viewed [here](https://mysite-gcqq.onrender.com/).

<info-block>This tutorial starts with a bare-bones installation and explains all required code modifications, so it should be straightforward to adapt it to any custom configuration in your existing Django codebase.</info-block>

## Create a Django Project

In this step, we will set up a local development environment and create basic project structure.

<warning-block>We will assume our project is called `mysite` and consistently use it throughout the code. Feel free to choose a different name, though it must be a [valid Python package name](https://www.python.org/dev/peps/pep-0008/#package-and-module-names).</warning-block>

### Install Poetry

If you do not have Poetry installed, follow the Poetry [installation instructions](https://python-poetry.org/docs/#installation) for your operating system. In most cases, you will simply need to run the following:

macOS/Linux:
```shell
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
```

Windows in PowerShell:
```powershell
(Invoke-WebRequest -Uri https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py -UseBasicParsing).Content | python
```

You may need to restart your shell to load the `poetry` command into your `PATH`. To verify the installation was successful, try running `poetry --version`.

### Create Project Structure

- Use [Poetry](https://python-poetry.org/docs/basic-usage/#project-setup) to initialize your project directory:

   ```shell{1,3,8}
   $ poetry new mysite
   Created package mysite in mysite
   $ cd mysite
   $ ls
   README.rst     mysite         pyproject.toml tests

   # We don't need the code for our package that was generated by Poetry, so:
   $ rm -rf mysite tests
   ```

- In `pyproject.toml`, ensure the Python version requirement includes version `3.7`, which is available on Render.

   ```toml{2}
   [tool.poetry.dependencies]
   python = "^3.7"
   ```

- Add Django as a dependency.

   ```shell
    $ poetry add django
   ```

- Create a Django project for your application.

   ```shell
   $ poetry run django-admin startproject mysite .
   ```

   You should end up with the following directory structure:

   ```shell
   $ tree
   .
   ├── README.rst
   ├── manage.py
   ├── mysite
   │   ├── __init__.py
   │   ├── asgi.py
   │   ├── settings.py
   │   ├── urls.py
   │   └── wsgi.py
   ├── poetry.lock
   └── pyproject.toml

   1 directory, 9 files
   ```

At this point, you should have a fully-functional scaffolding for your new Django application! To verify, you can start the development server.

```shell
$ poetry run ./manage.py runserver
```

<div style="width: 70%; margin: auto;">

![Django Successful Installation](https://render.com/static/4a76b00d1cb1412e04e54c2df0c5a671/b2aca/welcome.webp)

</div>

## Create the Hello World Landing Page

In this section, you will create a simple Django application with a static view, template, and an example static file that showcases the basic steps in creating a page using Django. If you are only interested in learning how to deploy a sample Django project to Render, you can skip to the **Update Your App For Render** section and deploy an empty project.

<info-block>If you wish to build something more complex, it's highly recommended to read the official [Writing your first Django app](https://docs.djangoproject.com/en/3.0/intro/tutorial01/) guide.</info-block>

### Create the Render app

Now that your application environment (a Django _project_) is ready, you are ready to start working on the _application_ itself.

<info-block>Django _projects_ are collections of _applications_, wired together to form a website. Django provides a number of built-in applications, with the [admin site](https://docs.djangoproject.com/en/3.0/ref/contrib/admin/) being a well-known example.</info-block>

- To create your application, run the following command from the root directory of your project:

   ```shell
   python manage.py startapp render
   ```

   This will create a directory named `render` with following contents:

   ```shell
   $ tree render
   render
   ├── __init__.py
   ├── admin.py
   ├── apps.py
   ├── migrations
   │   ├── __init__.py
   ├── models.py
   ├── tests.py
   └── views.py

   1 directory, 7 files
   ```

- You also need to inform Django about your new application. Open `mysite/settings.py`, find definition of the `INSTALLED_APPS` setting, and add a reference to the `RenderConfig` class at the beginning of the list:

   ```python{3}
   # https://docs.djangoproject.com/en/3.0/ref/settings/#installed-apps
   INSTALLED_APPS = [
       'render.apps.RenderConfig',
       'django.contrib.admin',
       'django.contrib.auth',
       ...
   ]
   ```

### Write Your First View

- In `render/views.py` add the following Python code:

   ```python
   from django.shortcuts import render

   def index(request):
       return render(request, 'render/index.html', {})
   ```

   This is one of the simplest views possible in Django. It renders the `render/index.html` template, which we will create in a later step.

- Create the `render/urls.py` file and add following code:

   ```python
   from django.urls import path

   from . import views

   urlpatterns = [
       path('', views.index, name='index'),
   ]
   ```

   This file will tell Django that you want your `index` view be accessible from the root URL of your application.

- Configure the _root_ project `urlpatterns` to point to the `urls` module of the `render` application. Open `mysite/urls.py`, add an import for `django.urls.include`, and `include` your application's URLs:

   ```python{2,6}
   from django.contrib import admin
   from django.urls import path, include

   urlpatterns = [
       path('admin/', admin.site.urls),
       path('', include('render.urls')),
   ]
   ```
- Create the `render/index.html` template you referenced earlier. Create a new file `render/templates/render/index.html` and add the following HTML:

   ```jinja2
   <!doctype html>
   <html lang="en">
   <head>
       <meta charset="UTF-8">
       <meta http-equiv="X-UA-Compatible" content="IE=edge" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0" />

       <title>Hello Django on Render!</title>

       <link rel="stylesheet"
             href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
             integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk"
             crossorigin="anonymous">
   </head>
   <body>
   <main class="container">
       <div class="row text-center justify-content-center">
           <div class="col">
               <h1 class="display-4">Hello World!</h1>
           </div>
       </div>
   </main>
   </body>
   </html>
   ```

- Add a static file to your application. Download this image and save it as `render/static/render/render.png`:

   ![render.png](https://render.com/images/render-banner.png)

- In your template, load the `static` module and reference the downloaded image:

   ```jinja2{1,7-11}
   {% load static %}

   <!doctype html>
   <html lang="en">
   ...
   <body>
   <header class="container mt-4 mb-4">
       <a href="https://render.com">
           <img src="{% static "render/render.png" %}" alt="Render" class="mw-100">
       </a>
   </header>
   ...
   </body>
   </html>
   ```

- You can now verify your app is working with the following command:

   ```shell
   $ python manage.py runserver
   ```

<div style="width: 70%; margin: auto;">

![Django Hello World](https://render.com/static/8f9532444d55b4ad06d02fee9b2e8e65/d63ad/hello-world.webp)

</div>

## Update Your App For Render

In order for your Django project to be ready for production, you will need to make a few adjustments in the application settings. You will update your project to use a [Render PostgreSQL database](https://render.com/docs/databases) instead of a SQLite database and configure [WhiteNoise](http://whitenoise.evans.io/en/stable/django.html) to serve your static files.

### Go Production-Ready

Before deploying any serious application onto production environment, you need to ensure it's properly secured and configured. Django documentation provides a useful [deployment checklist](https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/), which we will follow in this step.

- Open `mysite/settings.py` and find the declaration of the `SECRET_KEY` setting. We do not want to store production secrets in source code, so we'll fetch it from an environment variable that we'll create later:

   ```python{2}
   # SECURITY WARNING: keep the secret key used in production secret!
   SECRET_KEY = os.environ.get('SECRET_KEY', default='your secret key')
   ```

- Find the declaration of the `DEBUG` setting. This setting should _never_ be set to `True` in a production environment. You can detect if you are running on Render by checking if the `RENDER` environment variable is present in the [application environment](https://render.com/docs/environment-variables).

   ```python{2}
   # SECURITY WARNING: don't run with debug turned on in production!
   DEBUG = 'RENDER' not in os.environ
   ```

- When `DEBUG = False`, Django will not work without a suitable value for `ALLOWED_HOSTS`. You can get the name of your web service host from the `RENDER_EXTERNAL_HOSTNAME` environment variable, which is automatically set by Render.

   ```python{4-6}
   # https://docs.djangoproject.com/en/3.0/ref/settings/#allowed-hosts
   ALLOWED_HOSTS = []

   RENDER_EXTERNAL_HOSTNAME = os.environ.get('RENDER_EXTERNAL_HOSTNAME')
   if RENDER_EXTERNAL_HOSTNAME:
       ALLOWED_HOSTS.append(RENDER_EXTERNAL_HOSTNAME)
   ```

   <warning-block>If you add a custom domain to your Render app, don't forget to add your new domain to the list.</warning-block>

### Configure Django for PostgreSQL

For convenience, we will add the [DJ-Database-URL](https://github.com/jacobian/dj-database-url) package, which allows us to specify databases in Django using connection strings. Render Databases automatically provide connection strings in their control panel, which we will then provide to our web service via the `DATABASE_URL` environment variable. We will also need to add [psycopg2](https://www.psycopg.org/) to the project.

- Run following command to add necessary dependencies to your project:

   ```shell
   $ poetry add dj-database-url psycopg2-binary
   ```

- In `mysite/settings.py`, find declaration of the `DATABASES` setting and modify it to look as follows:

   ```python{2}
   # Don't forget to import dj-database-url at the beginning of the file
   import dj_database_url
   ```

   ```python{5-9}
   # Database
   # https://docs.djangoproject.com/en/3.0/ref/settings/#databases

   DATABASES = {
       'default': dj_database_url.config(
           # Feel free to alter this value to suit your needs.
           default='postgresql://postgres:postgres@localhost:5432/mysite',
           conn_max_age=600
       )
   }
   ```

### Static Files

Websites generally need to serve additional files such as images, JavaScript, and CSS. In Django, these files are referred to as [static files](https://docs.djangoproject.com/en/3.0/howto/static-files/deployment/) and it provides a dedicated module for collecting them into single place for serving in production.

The built-in module only supports moving files from one place to another, relying on web servers such as [Apache](https://httpd.apache.org/) or [NGINX](https://www.nginx.com/) to serve them to end users. On Render, the internet-facing web server is provided by default and we need a way to host static files using it. In this step, we will set up [WhiteNoise](http://whitenoise.evans.io) which is a highly popular solution for this problem. The following instructions are a short brief of the procedure described in the [WhiteNoise documentation](http://whitenoise.evans.io/en/stable/django.html).

- Add WhiteNoise as a dependency (adding [Brotli](https://en.wikipedia.org/wiki/Brotli) support is optional, but recommended):

   ```shell
   poetry add 'whitenoise[brotli]'
   ```

- Open `mysite/settings.py`, find the `MIDDLEWARE` list, and add the WhiteNoise middleware just after `SecurityMiddleware`:

   ```python{3}
   MIDDLEWARE = [
       'django.middleware.security.SecurityMiddleware',
       'whitenoise.middleware.WhiteNoiseMiddleware',
       ...
   ]
   ```

- Find the section where static files are configured. Apply following modifications:

   ```python{6,9,12,16}
   # Static files (CSS, JavaScript, Images)
   # https://docs.djangoproject.com/en/3.0/howto/static-files/

   # This setting tells Django at which URL static files are going to be served to the user.
   # Here, they well be accessible at your-domain.onrender.com/static/...
   STATIC_URL = '/static/'

   # Following settings only make sense on production and may break development environments.
   if not DEBUG:
       # Tell Django to copy statics to the `staticfiles` directory
       # in your application directory on Render.
       STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

       # Turn on WhiteNoise storage backend that takes care of compressing static files
       # and creating unique names for each version so they can safely be cached forever.
       STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
   ```

### Create a Build Script

- We need to run a series of commands to build our app. We can accomplish this with a build script. Create a script called `build.sh` at the root of your repository:

   ```bash
   #!/usr/bin/env bash
   # exit on error
   set -o errexit

   poetry install

   python manage.py collectstatic --no-input
   python manage.py migrate
   ```

   Make sure the script is executable before checking it into Git:

   ```bash
   $ chmod a+x build.sh
   ```

    We will configure Render to call this script on every push to the Git repository.

- We are going to run our application with [Gunicorn](https://gunicorn.org/). Add the dependency to your project:

   ```shell
   poetry add gunicorn
   ```

Commit all changes and push them to your GitHub repository. Now your application is ready to be deployed on Render!

## Deploy to Render

There are two ways to deploy your application on Render, either by [declaring your services within your repository](https://render.com/docs/infrastructure-as-code) using a `render.yaml` file or by manually setting up your services using the dashboard. In this tutorial, we will walk through both options.

### Use `render.yaml` for Deployments

- Create a file named `render.yaml` in the root of your directory. The file will define your Django **Web Service** and the [**Database**](https://render.com/docs/databases) used by your application. Don't forget to commit and push it to your remote repository.

   ```yaml
   databases:
     - name: mysite
       databaseName: mysite
       user: mysite

   services:
     - type: web
       name: mysite
       env: python
       buildCommand: "./build.sh"
       startCommand: "gunicorn mysite.wsgi:application"
       envVars:
         - key: DATABASE_URL
           fromDatabase:
             name: mysite
             property: connectionString
         - key: SECRET_KEY
           generateValue: true
         - key: WEB_CONCURRENCY
           value: 4
   ```

- On the Render Dashboard, go to the [YAML](https://dashboard.render.com/iacs) page and click `New From YAML` button. Select your application repository (give Render permission to access it if you haven't already) and click `Approve` on the next screen.

That's it! Your app will be live on your `.onrender.com` URL as soon as the build finishes.

<info-block>If you skipped the [Create the Hello World Landing Page](#create-the-hello-world-landing-page) section, you will see a `Not Found` error when visiting your site. You can verify your deploy was successful by visiting the admin dashboard at `/admin`</info-block>

### Manual Deployment

- Create a new [PostgreSQL database](https://render.com/docs/databases) on Render. Note your database **internal connection string**; you will need it later.

- Create a new **Web Service**, pointing it to your application repository (give Render permission to access it if you haven’t already).

- Select `Python` for the environment and set following properties:

| Property | Value |
| -------- | ----- |
| **Build Command** | `./build.sh` |
| **Start Command** | `gunicorn mysite.wsgi:application` |

- Add the following environment variables under **Advanced**:

Key                | Value                                                                 
 ------------------:| --------------------------------------------------------------------- 
`DATABASE_URL`     | The **internal connection string** for the database you created above 
`SECRET_KEY`       | Click `Generate` to get a secure random value                         
`WEB_CONCURRENCY`  | `4`                                                                   

That's it! Save your web service to deploy your Django application on Render. It will be live on your `.onrender.com` URL as soon as the build finishes.

### Create Django Admin Account

Once your application is live, create a new [Django admin account](https://docs.djangoproject.com/en/3.0/intro/tutorial02/#creating-an-admin-user) by running the following command in the Render Shell:

```shell
$ ./manage.py createsuperuser
```