DEV Community

Cover image for Deploying your Django 3 App
Jake Malachowski for Render

Posted on • Edited on

Deploying your Django 3 App

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 development environment, create a simple view, and deploy it to Render. The application will be configured to use a Render PostgreSQL database and will use Poetry 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 and the sample app can be viewed here.

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.

Create a Django Project

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

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.

Install Poetry

If you do not have Poetry installed, follow the Poetry installation instructions for your operating system. In most cases, you will simply need to run the following:

macOS/Linux:

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

Windows in 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 to initialize your project directory:
   $ 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.
   [tool.poetry.dependencies]
   python = "^3.7"
  • Add Django as a dependency.
    $ poetry add django
  • Create a Django project for your application.
   $ poetry run django-admin startproject mysite .

You should end up with the following directory structure:

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

$ poetry run ./manage.py runserver

Django Successful Installation

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.

If you wish to build something more complex, it's highly recommended to read the official Writing your first Django app guide.

Create the Render app

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

Django projects are collections of applications, wired together to form a website. Django provides a number of built-in applications, with the admin site being a well-known example.

  • To create your application, run the following command from the root directory of your project:
   python manage.py startapp render

This will create a directory named render with following contents:

   $ 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:
   # 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:
   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:
   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:
   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:
   <!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

  • In your template, load the static module and reference the downloaded image:
   {% 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:
   $ python manage.py runserver

Django Hello World

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 instead of a SQLite database and configure WhiteNoise 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, 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:
   # 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.
   # 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.
   # 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)

If you add a custom domain to your Render app, don't forget to add your new domain to the list.

Configure Django for PostgreSQL

For convenience, we will add the 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 to the project.

  • Run following command to add necessary dependencies to your project:
   $ poetry add dj-database-url psycopg2-binary
  • In mysite/settings.py, find declaration of the DATABASES setting and modify it to look as follows:
   # Don't forget to import dj-database-url at the beginning of the file
   import dj_database_url
   # 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 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 or NGINX 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 which is a highly popular solution for this problem. The following instructions are a short brief of the procedure described in the WhiteNoise documentation.

  • Add WhiteNoise as a dependency (adding Brotli support is optional, but recommended):
   poetry add 'whitenoise[brotli]'
  • Open mysite/settings.py, find the MIDDLEWARE list, and add the WhiteNoise middleware just after SecurityMiddleware:
   MIDDLEWARE = [
       'django.middleware.security.SecurityMiddleware',
       'whitenoise.middleware.WhiteNoiseMiddleware',
       ...
   ]
  • Find the section where static files are configured. Apply following modifications:
   # 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:
   #!/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:

   $ 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. Add the dependency to your project:
   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 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 used by your application. Don't forget to commit and push it to your remote repository.
   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 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.

If you skipped the 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

Manual Deployment

  • Create a new PostgreSQL database 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 by running the following command in the Render Shell:

$ ./manage.py createsuperuser

Top comments (3)

Collapse
 
mateusz__be profile image
Mateusz Bełczowski

Haven't heard about Render before, but looks interesting. Let's say that you have "classic" Django setup - docker compose with several services (Django, PostgreSQL, Celery, Redis, RabbitMQ, Daphne for handling websockets) - what would be the breakdown of the pricing to host entire application? How would you deploy that? :)

Collapse
 
jakemalachowski profile image
Jake Malachowski

Hey Mateusz!

The cost will be highly dependent on your resource requirements. From what you described, it seems like you would have:

One thing to call out where we are notably different from our competitors is that we don't currently have any limits on simultaneous connections or connection duration, including websockets!

Some of the other cool features that come with all Render services:

You can look at our pricing page to get an idea of how much it would cost based on your resource requirements.

I hope this helps! If you have any more questions, feel free to drop a message in our Slack channel.

Collapse
 
mateusz__be profile image
Mateusz Bełczowski

Awesome, thanks :)