DEV Community

Cover image for How to Deploy Django Applications on Heroku
It's pronounced W3SHY
It's pronounced W3SHY

Posted on

How to Deploy Django Applications on Heroku

Install Heroku CLI

Sign Up to Heroku

After installing the Heroku CLI, open a terminal and login to your account:

$ heroku login
heroku: Press any key to open up the browser to login or q to exit: 
heroku: Waiting for login... ⣻
Enter fullscreen mode Exit fullscreen mode

You're redirected to your browser and you login to Heroku with Heroku CLI and get a success message on your terminal

Creating App

First make sure you are in the root directory of the repository you want to deploy

Next create the heroku app from the terminal:

heroku create <your-app>
Enter fullscreen mode Exit fullscreen mode

Preparing the Application

Assumptions

  • You're familiar with the basics of Django. eg. concept of apps, settings, urls, basics of databases.
  • You have a Django application that you want to deploy to Heroku.
  • You are familiar with virtual environments
  • Your deployment Database is PostgreSQL

You will have to install several packages that will come in handy:

  • django-heroku pip install django-heroku
  • gunicorn pip install gunicorn
  • decouple pip install python-decouple
  • DATABASE_URL pip install dj-database-url
  • whitenoise pip install whitenoise

You will then add the following files in your application

  • Add a Procfile in the project root;
  • Add requirements.txt file with all the requirements in the project root;
  • Add requirements.txt with pip freeze > requirements.txt
  • A runtime.txt to specify the correct Python version in the project root;
  • Configure whitenoise to serve static files.

Now we go through each one of them

Procfile

Heroku apps include a Procfile that specifies the commands that are executed by the app’s dynos.
Create a file named Procfile in the project root with the following content:

web: gunicorn your_project_name.wsgi
Enter fullscreen mode Exit fullscreen mode
runtime.txt

This file contains the python version you are using for heroku to use, create runtime.txt in your project root and add your python version in the following format:

python-3.8.12
Enter fullscreen mode Exit fullscreen mode

You can specify which runtime to use for your app. List of Heroku Runtimes

Configuring Whitenoise: Django Static Files settings

It turns out django does not support serving static files in production. However, WhiteNoise project can integrate into your Django application, and was designed with exactly this purpose in mind.

Lets first configure static related parameter in settings.py

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'

# Extra places for collectstatic to find static files.
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
Enter fullscreen mode Exit fullscreen mode

Then add this line of code in the middleware section

MIDDLEWARE_CLASSES = (
    # Simplified static file serving.
    # https://warehouse.python.org/project/whitenoise/
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
Enter fullscreen mode Exit fullscreen mode

Add the following setting to settings.py in the static files section to enable gzip functionality.

# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

# configuring the location for media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# Configure Django App for Heroku.
django_heroku.settings(locals())
Enter fullscreen mode Exit fullscreen mode

python-decouple and dj-database-url

Python Decouple is a must have app if you are developing with Django. It’s important to keep your application credentials like API Keys, tokens and passwords.
dj-database-url is a simple Django utility that holds your Postgres database url

.env

Firts create a .env file and add it to .gitignoreso you don’t commit any sensitive data to your repository. Below is an example of configurations you can add to the .env file.

# An example, don't share your.env settings
SECRET_KEY='342s(s(!hsjd998sde8$=o4$3m!(o+kce2^97kp6#ujhi'
DEBUG=True
DB_NAME='db_name'
DB_USER='user'
DB_PASSWORD='db_password'
DB_HOST='127.0.0.1'
MODE='dev'
ALLOWED_HOSTS='<app name in heroku>.herokuapp.com'
DISABLE_COLLECTSTATIC=1
Enter fullscreen mode Exit fullscreen mode

We then edit settings.py to enable decouple to use the .env configurations.

import os
import django_heroku
import dj_database_url
from decouple import config,Csv

MODE=config("MODE", default="dev")
SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
# development
if config('MODE')=="dev":
   DATABASES = {
       'default': {
           'ENGINE': 'django.db.backends.postgresql_psycopg2',
           'NAME': config('DB_NAME'),
           'USER': config('DB_USER'),
           'PASSWORD': config('DB_PASSWORD'),
           'HOST': config('DB_HOST'),
           'PORT': '',
       }

   }
# production
else:
   DATABASES = {
       'default': dj_database_url.config(
           default=config('DATABASE_URL')
       )
   }

db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES['default'].update(db_from_env)

ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv())
Enter fullscreen mode Exit fullscreen mode

Deploying the app to Heroku

Create a postgres addon to your Heroku app

heroku addons:create heroku-postgresql:hobby-dev
Enter fullscreen mode Exit fullscreen mode

Next we log in to Heroku dashboard to access our app and configure it

Heroku dashboard
Add all your configurations in .env file directly to Heroku by running this command.

heroku config:set $(cat .env | sed '/^$/d; /#[[:print:]]*$/d')
Enter fullscreen mode Exit fullscreen mode

Remember to first set DEBUG to False and confirm that you have added all the configuration variables needed. Click on the Settings menu and then on the button Reveal Config Vars

Pushing to Heroku

Confirm that your app is running as expected before pushing. Runtime errors will cause deployment to fail so make sure you have no bugs and you have all the following; Procfile, requirements.txt with all required packages and runtime.txt.

Commit all the changes we have made and then:

git push heroku master
Enter fullscreen mode Exit fullscreen mode
  • If you are using main as the branch, change master to main

If you did everything correctly then the deployment should be done after a while with an output like this

Enumerating objects: 94, done.
Counting objects: 100% (94/94), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (84/84), done.
Writing objects: 100% (94/94), 3.35 MiB | 630.00 KiB/s, done.
Total 94 (delta 24), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Python app detected
remote: -----> Installing python-3.6.6
remote: -----> Installing pip
remote: -----> Installing requirements with pip
remote:        Collecting config==0.3.9 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 1))
remote:          Downloading https://files.pythonhosted.org/packages/0a/46/186ac016f3175211ec9bb4208579bc6dc9dd7dc882790d9f281533b83b0f/config-0.3.9.tar.gz
remote:        Collecting dj-database-url==0.5.0 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 2))
remote:          Downloading https://files.pythonhosted.org/packages/d4/a6/4b8578c1848690d0c307c7c0596af2077536c9ef2a04d42b00fabaa7e49d/dj_database_url-0.5.0-py2.py3-none-any.whl
remote:        Collecting Django==1.11 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 3))
remote:          Downloading https://files.pythonhosted.org/packages/47/a6/078ebcbd49b19e22fd560a2348cfc5cec9e5dcfe3c4fad8e64c9865135bb/Django-1.11-py2.py3-none-any.whl (6.9MB)
remote:        Collecting django-bootstrap3==10.0.1 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 4))
remote:          Downloading https://files.pythonhosted.org/packages/18/a8/f12d8491155c7f237084b883b8600faf722e3a46e54f17a25103b0fb9641/django-bootstrap3-10.0.1.tar.gz (40kB)
remote:        Collecting django-heroku==0.3.1 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 5))
remote:          Downloading https://files.pythonhosted.org/packages/59/af/5475a876c5addd5a3494db47d9f7be93cc14d3a7603542b194572791b6c6/django_heroku-0.3.1-py2.py3-none-any.whl
remote:        Collecting gunicorn==19.9.0 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 6))
remote:          Downloading https://files.pythonhosted.org/packages/8c/da/b8dd8deb741bff556db53902d4706774c8e1e67265f69528c14c003644e6/gunicorn-19.9.0-py2.py3-none-any.whl (112kB)
remote:        Collecting Pillow==5.2.0 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 7))
remote:          Downloading https://files.pythonhosted.org/packages/d1/24/f53ff6b61b3d728b90934bddb4f03f8ab584a7f49299bf3bde56e2952612/Pillow-5.2.0-cp36-cp36m-manylinux1_x86_64.whl (2.0MB)
remote:        Collecting psycopg2==2.7.5 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 8))
remote:          Downloading https://files.pythonhosted.org/packages/5e/d0/9e2b3ed43001ebed45caf56d5bb9d44ed3ebd68e12b87845bfa7bcd46250/psycopg2-2.7.5-cp36-cp36m-manylinux1_x86_64.whl (2.7MB)
remote:        Collecting python-decouple==3.1 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 9))
remote:          Downloading https://files.pythonhosted.org/packages/9b/99/ddfbb6362af4ee239a012716b1371aa6d316ff1b9db705bfb182fbc4780f/python-decouple-3.1.tar.gz
remote:        Collecting pytz==2018.5 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 10))
remote:          Downloading https://files.pythonhosted.org/packages/30/4e/27c34b62430286c6d59177a0842ed90dc789ce5d1ed740887653b898779a/pytz-2018.5-py2.py3-none-any.whl (510kB)
remote:        Collecting whitenoise==3.3.1 (from -r /tmp/build_19aebf8f25d534a39e73b13219af9927/requirements.txt (line 11))
remote:          Downloading https://files.pythonhosted.org/packages/0c/58/0f309a821b9161d0e3a73336a187d1541c2127aff7fdf3bf7293f9979d1d/whitenoise-3.3.1-py2.py3-none-any.whl
remote:        Installing collected packages: config, dj-database-url, pytz, Django, django-bootstrap3, whitenoise, psycopg2, django-heroku, gunicorn, Pillow, python-decouple
remote:          Running setup.py install for config: started
remote:            Running setup.py install for config: finished with status 'done'
remote:          Running setup.py install for django-bootstrap3: started
remote:            Running setup.py install for django-bootstrap3: finished with status 'done'
remote:          Running setup.py install for python-decouple: started
remote:            Running setup.py install for python-decouple: finished with status 'done'
remote:        Successfully installed Django-1.11 Pillow-5.2.0 config-0.3.9 dj-database-url-0.5.0 django-bootstrap3-10.0.1 django-heroku-0.3.1 gunicorn-19.9.0 psycopg2-2.7.5 python-decouple-3.1 pytz-2018.5 whitenoise-3.3.1
remote: 
remote: -----> Discovering process types
remote: 
remote: -----> Compressing...
remote:        Done: 56.3M
remote: -----> Launching...
remote:        Released v6
remote:        https://mtr1bune.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
To https://git.heroku.com/mtr1bune.git
 * [new branch]      master -> master
Enter fullscreen mode Exit fullscreen mode

Run Migrations

heroku run python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

If you wish to push your postgres database data to Heroku then run

heroku pg:reset
heroku pg:push <The name of the db in the local psql> DATABASE_URL --app <heroku-app>
Enter fullscreen mode Exit fullscreen mode

You can the open the app in your browse.

Last comment

Don't beat yourself up when you find it challenging to memorize language or library syntax. They have documentation for a reason, so feel free to reference it. The syntax will stick to your memory on frequent use.💯✍️

tweet

Top comments (0)