How to deploy a Django web application to Heroku - a comprehensive guide

Subscribe to my newsletter and never miss my upcoming articles

The process of taking a project from a local machine to the internet is in many ways a magical one. Heroku is a platform-as-a-service solution that offers simple deployment options to developers. This article will present a step-by-step guide to deploying Django applications on Heroku.

Deployment setup

First, ensure that you have the Heroku CLI installed. If you do not, visit this link for installation instructions.

heroku -v

Log into Heroku, by using the command below. This should open up your default browser, and prompt you to authenticate via the click of a button.

heroku login

Install gunicorn, a tool that allows you to run a light-weight web server. This is essential for deployment on the Heroku platform.

 pip install gunicorn

Create a Procfile. Think of this as a way to list the process types within your web application. From the command, it can be seen that this works together with the gunicorn package earlier installed. Replace <project-name> with the name used when creating the Django project.

web: gunicorn <project-name>.wsgi --log-file -

Add a runtime.txt file. This helps Heroku know what Python runtime you would like to use to run your application. I prefer to use the runtime I work with on my development machine. This has worked for me across a variety of deploys so far.

python-3.7.4

Application creation

Create an Heroku application with the following command. Here <app-name> will be replaced with the name of choice for your web app.

heroku apps:create <app-name>

The next thing to do is creating a database. This guide favours PostgreSQL. Here is the relevant command.

heroku addons:create heroku-postgresql:hobby-dev --version=11 --app <app-name>

If your web application is like most others, you will need to serve static files as well. An efficient tool for this can be installed as follows.

pip install whitenoise

Whitenoise offers a number of configuration options for Django. I have found that the following works best on Heroku with most deployments. Place this at the base of the settings file, along with the other static configurations.

# Compressed, but not cached.

STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'

It is essential to include the WhiteNoise middleware as well. This is done by placing it directly after the security middleware, and before all others. This is shown directly below.

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]

This is a good time to set up your Django app to use environment variables. A package that helps greatly with this is django-environ. Install this via pip, and add the following to your settings.py file:

import environ

# Environment settings
env = environ.Env(
    DEBUG=(bool, False)
)
environ.Env.read_env()  # reading .env file

DEBUG = env('DEBUG')  # False, if not set in env

Place your .env file in the same directory location as your settings.py - remember to include it in your .gitignore file.

Remember to set up your ALLOWED_HOSTS in such a way as to create a list of valid addresses. This should be done with an environment variable. This could result in an application error if not done properly.

Creating a new Secret Key

Upon creating a Django web application a SECRET_KEY is automatically generated. In the haste of making that first commit, this can inadvertently be added to one's git history. This is a security nightmare waiting to happen.

You can fix this by doing the following:

  • Access the python shell made available via Django.
python manage.py shell
  • Access the get_random_secret_key method that comes with Django and produce a new string. This can be done repeatedly - if necessary.
from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())

You can save the new value generated above to an environment variable, and do the same in the settings section of your Heroku deployment(s).

Database setup

At this point, you will need to acquire the link to the Postgres database created earlier. In the place of <app-name>, use the name you chose when creating your application on Heroku.

heroku config:get DATABASE_URL --app <app-name>

With the newly acquired database link, you will now set this as a configuration variable via the command below. This links your Django web application once deployed to the Postgres database created.

heroku config:add DATABASE_URL=<DATABASE_URL value> --app <app-name>

Your default database config should look like this.

# default config

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

This needs to be replaced with suitable Postgres configuration that works with your Heroku deployment.

DB_INFO = db_parser(os.environ['DATABASE_URL'])

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': DB_INFO['name'],
        'USER': DB_INFO['user'],
        'PASSWORD': DB_INFO['password'],
        'HOST': DB_INFO['host'],
        'PORT': DB_INFO['port'],
    }
}

For the Postgres configuration, I present to you a simple parser file. It extracts the values needed for essential fields in Databases' dictionary. These values are from the DATABASE_URL provided by Heroku.

What I would advise is creating a conditional that switches between these two configurations based on the value of an environment variable. This is not totally 12factor but helps with swift development setup.

pip install psycopg2-binary

As a final step install the package above to help your Django application work with Postgres. According to the PyPI page, "Psycopg is the most popular PostgreSQL database adapter for the Python programming language".

Final steps

Ensure for good measure that your Heroku app has been initialized via git. The following command helps with this:

git remote -v

You should see a remote named heroku with a git file with name similarities to the application you created earlier.

If you do not see this, simply add the Heroku app manually. Do this by taking the link to the git repository in the settings section of your deployment. With this link, run the following command, replacing <Heroku git URL> with the link acquired.

git remote add heroku <Heroku git URL>

Next, disable COLLECTSTATIC for the application. This prevents Heroku from collecting static files on your behalf. If not disabled, this can result in build failures, if static file collection does not succeed for whatever reason.

heroku config:set DISABLE_COLLECTSTATIC=1

One of the most important final steps involves creating a requirements file. This is necessary to properly run the web application.

pip freeze > requirements.txt

We have arrived at the moment all of this has been progressing towards - deployment. The command is as follows:

git push heroku master

We will need to start a web dyno, which is essentially the server of sorts. This is where our application will run on the Heroku platform.

heroku ps:scale web=1

Remember to migrate the changes made during development on the deployment database as well.

heroku run python manage.py migrate

Never run makemigrations on Heroku directly as this has little effect in the long-term. The filesystem on the platform is ephemeral and will not retain your changes beyond the moments during which your app is running.

For this reason, it is best to commit your migrations to git, and simply migrate these on the platform directly.

heroku run python manage.py createsuperuser

Above is Heroku's flavour of the quintessential Django command. Create your superuser and your app is good to go. Configured and deployed on the Heroku platform - in the most comprehensive and efficient way.

You might find these settings for a sample project useful. It is configured for Heroku deployment.


Cover base from Unsplash

No Comments Yet