Deploy Django applications in Heroku

Francisco Alejandro Rojas
6 min readFeb 7, 2021

As the title indicates, this is just one more tutorial on how to deploy django applications in heroku. In this tutorial you will learn a little about everything and a little about nothing, which will lead you to dig into some things that you are simply told to do without going into much detail. Before I start I am going to assume you have Python, pip, Virtualenv, Git and Heroku Cli installed and that you use a version of windows .

The versions that I use of each tool are the most current at the time of publishing this tutorial, although this does not apply to the git version.

  • python 3.9.1
  • pip 21.0.1
  • virtualenv 20.4.1
  • git 2.28.0
  • heroku cli 7.47.11

Let’s go

Create virtual environment, install dependencies, create project and application, initialize repository and create some files necessary for deployment in Heroku

  • Open the command prompt
  • Create a folder in which all the project code will be stored
C:\Users\fralejanro> mkdir mysite
  • Change to mysite folder and create virtual environment
C:\Users\fralejanro> cd mysite
C:\Users\fralejanro\mysite> virtualenv mysite_env
  • Activate virtual environment
C:\Users\fralejanro\mysite> mysite_env\Scripts\activate
  • Install Django
(mysite_env) C:\Users\fralejanro\mysite> pip install django
  • Install Whitenoise
(mysite_env) C:\Users\fralejanro\mysite> pip install whitenoise
  • Install Gunicorn
(mysite_env) C:\Users\fralejanro\mysite> pip install gunicorn
  • Install dj-database-url
(mysite_env) C:\Users\fralejanro\mysite> pip install dj-database-url
  • Install Pillow
(mysite_env) C:\Users\fralejanro\mysite> pip install --upgrade Pillow
  • Install Psycopg2
(mysite_env) C:\Users\fralejanro\mysite> pip install psycopg2
  • Create a django project
(mysite_env) C:\Users\fralejanro\mysite> django-admin startproject mysite_project
  • Rename the folder mysite_project to mysite_root and change to the folder mysite_root
(mysite_env) C:\Users\fralejanro\mysite> ren mysite_project mysite_root
(mysite_env) C:\Users\fralejanro\mysite> cd mysite_root
  • Initialize a Git repository
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> git init
  • Create a file to specify the intentionally untracked files that Git should ignore
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo. > .gitignore
  • In the .gitignore file write the following
*.pyc
*.sqlite3
.vscode
__pycache__/
settings_dev.py
  • Create the file that will contain all the dependencies of your project
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> pip freeze > requirements.txt
  • Create a file with the name of runtime.txt and write in it the version of python you are using
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo python-3.9.1> runtime.txt
  • Create a file with the name Procfile and define in it the process types and the application entry points
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo web: gunicorn mysite_project.wsgi> Procfile
  • Create an application, remember that a project and an application are two different things
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> django-admin startapp blog
  • Create a folder for your base templates and add a .keep file for git to keep track of the folder
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> mkdir templates
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo. > templates/.keep
  • Create a folder for your static files
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> mkdir static
  • Create a folder for your additional static files and add a .keep file for git to keep track of the folder
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> mkdir staticfiles
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo. > staticfiles/.keep
  • Create a folder for user uploaded files and add a .keep file for git to keep track of the folder
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> mkdir media
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> echo. > media/.keep

Configuring the settings.py file and creating the settings_dev.py file

  • Open the settings.py file from the mysite_project folder with a code editor and make the following modifications
  • Add your application to the list of installed applications
INSTALLED_APPS = [
'blog.apps.BlogConfig',
'django.contrib.admin',
# more apps
]
  • Import the os module
import os
  • Set the folder path where your base templates are searched
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# ...
  • Set the folder path where your static files are stored after using python manage.py collectstatic
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  • Set the folder path where your additional static files are stored
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'staticfiles'),
]
  • Set the folder path where user uploaded files will go
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • Enable Whitenoise. The WhiteNoise middleware should be placed directly after Django SecurityMiddleware (if you are using it) and before any other middleware
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
# ...
]
  • Add compression and caching support
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
  • Configure the proxy to set a custom HTTP header that tells Django if the request came through HTTPS
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
  • At this point you need to create a copy of the settings.py file that is located inside the mysite_project folder, save the copy with the name of settings_dev.py in the mysite_project folder
  • Configure the local postgresql database, in the settings_dev.py file located inside the mysite_project folder. This is only necessary if you are not using the default sqlite database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'your_database',
'USER': 'postgres',
'PASSWORD': 'your_password',
'HOST': 'localhost',
'PORT': '5432',
}
}

Now continue editing the settings.py file found in the mysite_project folder

  • Configure the heroku database with the dj-database-url library
import dj_database_urlDATABASES = {
'default': dj_database_url.config(conn_max_age=600, ssl_require=True)
}
  • Set the SECRET_KEY so that its value is set from a Heroku config var
SECRET_KEY = os.getenv('SECRET_KEY')
  • Change the value of DEBUG to False and set allowed hosts
DEBUG = False

ALLOWED_HOSTS = ['.herokuapp.com']

Configuring the urls

  • Create a file with the name of urls.py in the blog folder
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> cd blog
(mysite_env) C:\Users\fralejanro\mysite\mysite_root\blog> echo. > urls.py
  • Open the file you just created and type the following
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static
from . import views

urlpatterns = [
]urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
  • Now open the urls.py file found in the mysite_project folder and add a url to get the urls from the urls.py file of the blog application
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]

Collect static files and migrate

  • Change to mysite_root folder and collect static files
(mysite_env) C:\Users\fralejanro\mysite\mysite_root\blog> cd ..
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> python manage.py collectstatic --settings=mysite_project.settings_dev
  • Migrate
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> python manage.py migrate --settings=mysite_project.settings_dev

You’re almost done, just a few more steps

Commit, create app, config vars and deploy in Heroku

  • Add files to trace and make your first commit
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> git add .
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> git commit -m "Init App"
  • Login to heroku
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> heroku login
  • Create a new application in heroku with a Python build package
(mysite_env) C:\Users\fralejanro\mysite\mysite_root>heroku create mysitemedium --buildpack heroku/python
  • Create a var for your SECRET_KEY, the value of the SECRET_KEY is in the settings_dev.py file that is inside the mysite_project folder. The value of the SECRET_KEY must be enclosed in quotes, the quotes are not part of the SECRET_KEY
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> heroku config:set SECRET_KEY="your_secret_key" -a mysitemedium
  • Create a var so that heroku does not execute collectstatic and does not serve its static files
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> heroku config:set DISABLE_COLLECTSTATIC=1 -a mysitemedium
  • Add a remote control to the local repository with the name of the application
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> heroku git:remote -a mysitemedium
set git remote heroku to https://git.heroku.com/mysitemedium.git
  • Use the command to insert the code of the master branch of the local repository into the remote of heroku
(mysite_env) C:\Users\fralejanro\mysite\mysite_root> git push heroku master
Enumerating objects: 306, done.
Counting objects: 100% (306/306), done.
Delta compression using up to 2 threads
Compressing objects: 100% (299/299), done.
Writing objects: 100% (306/306), 989.00 KiB | 142.00 KiB/s, done.
Total 306 (delta 37), reused 0 (delta 0), pack-reused 0
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-20 stack
remote: -----> Python app detected
remote: -----> Installing python-3.9.1
remote: -----> Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote: Collecting asgiref==3.3.1
remote: Downloading asgiref-3.3.1-py3-none-any.whl (19 kB)
remote: Collecting Django==3.1.6
remote: Downloading Django-3.1.6-py3-none-any.whl (7.8 MB)
remote: Collecting gunicorn==20.0.4
remote: Downloading gunicorn-20.0.4-py2.py3-none-any.whl (77 kB)
remote: Collecting Pillow==8.1.0
remote: Downloading Pillow-8.1.0-cp39-cp39-manylinux1_x86_64.whl (2.2 MB)
remote: Collecting psycopg2==2.8.6
remote: Downloading psycopg2-2.8.6.tar.gz (383 kB)
remote: Collecting pytz==2021.1
remote: Downloading pytz-2021.1-py2.py3-none-any.whl (510 kB)
remote: Collecting sqlparse==0.4.1
remote: Downloading sqlparse-0.4.1-py3-none-any.whl (42 kB)
remote: Collecting whitenoise==5.2.0
remote: Downloading whitenoise-5.2.0-py2.py3-none-any.whl (19 kB)
remote: Building wheels for collected packages: psycopg2
remote: Building wheel for psycopg2 (setup.py): started
remote: Building wheel for psycopg2 (setup.py): finished with status 'done'
remote: Created wheel for psycopg2: filename=psycopg2-2.8.6-cp39-cp39-linux_x86_64.whl size=523826 sha256=34d4bb8d577ff4b3800538fb53098c6e917e07d6bf12662678c25b4821ca67c1
remote: Stored in directory: /tmp/pip-ephem-wheel-cache-oqpf3z_g/wheels/a2/07/10/a9a82e72d50feb8d646acde6a88000bbf2ca0f82e41aea438a
remote: Successfully built psycopg2
remote: Installing collected packages: asgiref, pytz, sqlparse, Django, gunicorn, Pillow, psycopg2, whitenoise
remote: Successfully installed Django-3.1.6 Pillow-8.1.0 asgiref-3.3.1 gunicorn-20.0.4 psycopg2-2.8.6 pytz-2021.1 sqlparse-0.4.1 whitenoise-5.2.0
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote:
remote: -----> Compressing...
remote: Done: 62.9M
remote: -----> Launching...
remote: Released v7
remote: https://mysitemedium.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/mysitemedium.git
* [new branch] master -> master

If you have an output like the previous one, it means that you already have your Django application deployed in Heroku. To verify this, you can enter the administrator view of your application, your url should be similar to this https://mysitemedium.herokuapp.com/admin, since if you enter only your application you will get “The requested resource was not found on this server.” And it is good that this happens since we do not define any associated view that url.

All the application code is in the following repository fralejanro/mysite: Deploy Django applications in Heroku (github.com)

--

--