DEV Community 👩‍💻👨‍💻

Cover image for Django + PostgreSQL Deployment on Heroku
Meet Rajesh Gor
Meet Rajesh Gor

Posted on • Originally published at meetgor.com

Django + PostgreSQL Deployment on Heroku

Introduction

Django projects are quite easy to build and simple to understand, you might have created a Django application and wanted to show it to the world? You can deploy a basic Django application with a database(PostgreSQL) with Heroku. It provides a decent free tier with some great features and add-ons. A free tier Heroku account has a limitation of 5 apps, limited data in the database, limited connections to the server per month, and so on.

Though the free tier is not a great option for bigger applications, it suits really well for smaller scale and ide projects, so we will be looking into the details of how to deploy a Django application to Heroku which is a Platform as Service (PaS).

This series will be an extension of the series Django basics which covered the basics of the Django framework, we covered from basic Django fundamentals to building a CRUD API. In this series, we will be exploring some platforms for giving a better understanding of how things work and understanding a few components that were left as default while understanding the basics of Django. Let's get started with Django Deployment!

Creating a Django Application

For deploying an app, we definitely need an app, we need to create a basic Django application to deploy on the web. We'll be creating a simple blog application with a couple of views and a simple model structure. As for the database, we'll be using Postgres as Heroku has an add-on for it and it is pretty easy to configure.

Set up a virtual environment

We need to set up a virtual environment in order to keep the Django project neat and tidy by managing the project-specific dependencies and packages. We can use the virtualenv package to isolate a python project from the rest of the system.

# install the virtualenv package
pip install virtualenv

# create a virtual env for the project
virtualenv .venv

# activate the virtualenv
Windows:
.venv\Scripts\activate

Linux/macOS:
source .venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

This will set up the project nicely for a Django project, you now install the core Django package and get started with creating a Django application.

# install django
pip install django

# start a django project
django-admin startproject blog .

cd blog

# create a application in django project
python manage.py createapp api

# Create some models, views, URLs, templates

# run the server
python manag.py runserver
Enter fullscreen mode Exit fullscreen mode

We assume you already have a Django project configured with some basic URLs, views and templates or static files as per your project and requirements, for this tutorial I will be using the simple blog application from my previous Django tutorials as a reference. You can follow along with my Django Basics series and refer to the Blog Application project on GitHub.

Configuring the Django Application

Make sure to create and activate the virtual environment for this django project. This should be done to manage the dependencies and packages used in the project. If you are not aware of the virtual environment and django setup, you can follow up with this post.

Creating a runtime.txt file

Now, Firstly we need to specify which type and version of language we are using. Since Django is a Python-based web framework, we need to select the python version in a text file.

runtime.txt

python-3.9.5
Enter fullscreen mode Exit fullscreen mode

Here, the version can be anything as per your project and the packages installed.

Creating requirements.txt file

We'll first create a requirements.txt file for storing all the dependencies and packages installed in the application. This will help in installing dependencies while deploying the application. We can either use a requirements.txt file using virtualenv or a Pipfile using Pipenv. Both serve the same purpose but a bit differently.

Assuming you are in an isolated virtual environment for this Django project, you can create a requirements.txt file using the below command:

Make sure the virtualenv is activated before running the command:

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

This will create a simple text file that contains the package names along with the versions used in the current virtual environment. A simple Django requirements file would look something like this:

asgiref==3.4.1
Django==3.2.11
pytz==2021.3
sqlparse==0.4.2
typing_extensions==4.0.1
Enter fullscreen mode Exit fullscreen mode

image.png

This is vanilla Django without any additional dependencies but if you have integrated other functionalities like Django Rest Framework, PostgreSQL, Crispy Forms, Schedulers, etc. there might be additional dependencies that become quite crucial for the smooth working of the project.

If you are using pipenv, you don't need to make any efforts to manually activate and manage virtual environment, the dependencies are installed and taken care of by the pipenv installer. You just need to make sure to install any package with pipenv install and not pip install for better and improved package tracking.

So, that's all we need to take care of packages and keep a list of these integrated packages for the project. You need to update the requirements.txt file every time you install any new package or modify the dependencies for a project. Simply use the command pip freeze > requirements.txt in the activated virtual environment.

Creating a Procfile

Next up, we have the Procfile, a procfile is a special file that holds information about the processes to be run to start or activate the project. In our case, for django we need a web process that can manage the server.

A Procfile is a simple file without any extension, make sure to write Procfile as it is as the name of the file in the root folder of the project. Inside the file add the following contents:

Procfile

web: gunicorn <project_name>.wsgi
Enter fullscreen mode Exit fullscreen mode

As we can see we have defined the web process using gunicorn, Gunicorn is a python package that helps in creating WSGI HTTP Server for the UNIX operating systems. So, we need to install the package and update the package dependency list.

pip install gunicorn

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

That would be good to go for creating and serving up the project while deploying the project on Heroku.

Creating a Heroku App

A Heroku App is basically like your Django Project, you can create apps for deploying your django projects on Heroku. You are limited to 5 apps on the Free tier but that can be expanded on the paid plans.

image.png

The name of your heroku app should be unique globally, you need to try a few combinations before a good one fits your project. This name has no significance on your django project code, though it would be the name from which you would access the web application as a name <app-name>.herokuapp.com.

So, choose it wisely if you are not attaching a custom domain. You can attach a custom domain, you can navigate to the domain section in the settings tab.

Setting up the database

To set up and configure a database in django on Heroku, we need to manually acquire and attach a PostgreSQL add-on to the heroku app.

  • Firstly locate to the Resources Tab in your Heroku app.
  • Search postgres in the Add-ons Search bar
  • Click on the Heroku Postgres Add-on
  • Submit the Order Form and you have the add-on enabled in the app.

image.png

image.png

Alternately, you can use the Heroku CLI,

Heroku CLI Setup

You can use the Heroku CLI which is a command-line interface for managing and creating Heroku applications. You need to first install the CLI by heading towards the heroku documentation. Once the CLI is installed, you need to login into your Heroku account by entering the following command:

heroku login

Enter fullscreen mode Exit fullscreen mode

image.png

This will allow us to work with heroku commands and manage our heroku application from the command line itself. The below command will create a add-on for heroku-postgres for the application provided as the parameter options

heroku addons:create heroku-postgresql:hobby-dev --app <app_name>
Enter fullscreen mode Exit fullscreen mode

image.png

This should hopefully add a fresh instance of a postgres database for your heroku app. You can now configure the database for your project, we simply need the Database URL from the heroku app dashboard. We'll see how to configure the environment variables in Django for Heroku to keep your secrets like the SECRET_KEY, DATABSE_URL, etc.

If you want MySQL as a database, you can check out the ClearDB Add-On for Heroku to simply attach it like Postgres on your Heroku application. Also, if you wish to add MongoDB into your Django application on Heroku, you can use Object Rocket(OR Mongo). It is not available in the free tier though, unlike PostgreSQL and MySQL.

Configuring Environment Variables

We need to keep our secrets for the django project out of the deployed code and in a safe place, we can create environment variables and keep them in a .env file which will be git-ignored. We do not want this .env file to be open source and thus should not be committed.

We first need to create a new secret key if you already have a GitHub repository, chances are you would have committed the default secret key open for the world to see, it is an insecure way of deploying django apps in production.

So, open up a terminal and launch a python REPL:

python

import secrets
secrets.token_hex(24)
Enter fullscreen mode Exit fullscreen mode

image.png

This should generate a secret key that only you know now. So, just copy the key without the quotes and create a file .env in the root project folder.

.env

SECRET_KEY=76419fd6885a677f802fd1d2b5acd0188e23e001042b05a8
Enter fullscreen mode Exit fullscreen mode

The .env file should also be added to the .gitignore file, so simply append the following in the .gitignore file

.env
Enter fullscreen mode Exit fullscreen mode

This file is only created to test the project locally, so we need to also make this key available on heroku. For doing that we need to add Config Variables to the heroku app.

  • Locate to the Settings Tab in your Heroku Application Dashboard
  • We have the Config Vars section in the located tab = We need to reveal those variables and we will be able to see all the variables.

image.png

We already have a DATABASE_URL variable declared when we attached the django-postgres database to our application. We will now add one more variable to the Application, i.e. the SECRET_KEY. Simply, enter the name of the key and also enter the value of the key, so basically a key-value pair. Simply click on the Add button and this will add the variable to your application.

image.png

You also need to copy the DATABSE_URL into our local setup file(.env file). Copy the Database URL and paste it into the .env file as follows:

DATABASE_URL=postgres://sjxgipufegmgsw:78cbb568e@ec2-52-4-104-184.compute-1.amazonaws.com:5432/dbmuget
Enter fullscreen mode Exit fullscreen mode

The format for the postgres URL is as follows:

postgresql://[user[:password]@][netloc][:port][/dbname]
Enter fullscreen mode Exit fullscreen mode

We have now created environment variables for our django application and also added config vars in the heroku app, we now need a way to parse these environment variables into the Django project.

Parsing Environment variables using python-dotenv

We will use python-dotenv to parse variables into the django settings configurations like SECRET_KEY and DATABASES.

  • Install python-dotenv with pip with the command :
pip install python-dotenv
Enter fullscreen mode Exit fullscreen mode

We need to then modify the default variables in the settings.py file. Firstly, we will load in the .env file for accessing the environment variables for the configuration of the project.

Append the following code, to the top of the settings.py file, make sure don't override the configuration so remove unnecessary imports and configurations.

# <project_name>/settings.py

import os
from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent.parent

load_dotenv(os.path.join(BASE_DIR, ".env"))

Enter fullscreen mode Exit fullscreen mode

We have imported the package dotenv basically the python-dotenv into the settings.py file and the module os for loading the .env file. The load_dotenv function helps in loading the key-value pairs which are the configuration variables that can act as actual environment variables. We provide in a file to the load_dotenv function which is the .env file in our case, you can pick up any location for the .env file but make sure to change the location while parsing the file into the load_dotenv function.

After loading the variables into the settings.py file, we now need to access those variables and set the appropriate variables the configuration from the variables received from the load_dotenv function. The os.getenv function to access the environment variables. The os.getenv function takes a parameter as the key for the environment variable and returns the value of the environment variable.

SECRET_KEY = os.getenv("SECRET_KEY")
Enter fullscreen mode Exit fullscreen mode

We have secretly configured the SECRET_KEY for the django project. If you have any other variables as simple key-value pairs like AUTH passwords, username, etc. you can use this method to get the configuration variables.

Loading Database configuration

Databases are a bit different as compared to other simple configurations in django. We need to make a few adjustments to the default database configuration. We will install another package dj-database-url to configure DATABASE_URL. Since the databse_url has a few components we need a way to extract the details like the hostname, port, database_name, and password. Using the dj-database-url package we have a few functions that can serve the purpose.

pip install dj-database-url
Enter fullscreen mode Exit fullscreen mode

At the end of your settings.py file, append the following code.

import dj_database_url

DATABASE_URL = os.getenv("DATABASE_URL")

DATABASES = {
    "default": dj_database_url.config(default=DATABASE_URL, conn_max_age=1800),
}
Enter fullscreen mode Exit fullscreen mode

We would need an adapter for making migrations to the PostgreSQL database i.e. the psycopg2 package. This is a mandatory step if you are working with postgres database. This can be installed with the simple pip install:

pip install psycopg2

# If it does not work try
pip install psycopg2-binary


# if still error persists try installing setuptools
pip install -U setuptools
pip install psycopg2
Enter fullscreen mode Exit fullscreen mode

Now, that we have configured the database, we can now apply migrations to the fresh database of postgres provided by heroku. We will simply run the migrate command and in the heroku app the PostgreSQL database would have been modified and an appropriate schema should be applied.

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

image.png

Make sure to update the requirements.txt file before pushing the project to Heroku for making sure everything works as expected. Since we have installed a few additional packages that are directly used in the settings.py file, we need to run the pip freeze command to update the requiremnets.txt file.

Serving Static Files

Now, if you have some static files like CSS, Javascript, or images, you need to configure the staticfiles in order to serve them from the heroku server. We will require another config variable for collecting the static files from the selected repository.


STATIC_URL = "static/"
STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

Enter fullscreen mode Exit fullscreen mode

Here, if you have served your static files from the static folder in the root directory of your django project, you can add the above code in the settings.py file. We will collect all static files in the folder along with the default static files provided by django in the staticfiles directory. Run the following command if you want to test whether the static files are properly collected and served.

python manage.py collectstatic 
Enter fullscreen mode Exit fullscreen mode

image.png

So, this command will collect all the static files and store them in a single place. We see that the files from the admin section are also copied as well as the custom static files from the project configuration. Now, we can move on to set the config variable for the heroku app.

DISABLE_COLLECTSTATIC = 0
Enter fullscreen mode Exit fullscreen mode

We can set the DISABLE_COLLECTSTATIC variable as 0 or 1 indicating whether to disable it or not. We have currently enabled the static file collection while deploying the app but you can set it to 1 to disable the collection of static files.

image.png

Since I first tested the application on heroku, the static files don't work as expected, we need another package to make sure the staticfiles are served property. We will be installing the whitenoise package which serves as the middleware for serving the static files.

pip install whitenoise
Enter fullscreen mode Exit fullscreen mode

Add the whitenoise middleware whitenoise.middleware.WhiteNoiseMiddleware to the MIDDLEWARE list in the settings.py file.

MIDDLEWARE = [
...
...
...
    'whitenoise.middleware.WhiteNoiseMiddleware',
]

Enter fullscreen mode Exit fullscreen mode

That should be enough to make the most of the deployment on heroku. You will have to make a few adjustments as per your requirements and project.

Deploy from GitHub

We are now all set to deploy the application on Heroku, we can use the Connect to GitHub or Heroku CLI to push the code into production. Heroku CLI is quite easy with a few sets of commands but if your project is deployed on GitHub, you can straightaway let the deployment start the build on a push to a specific branch. This becomes quite automotive and easy to scale while deploying a large-scale application.

pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

This step is quite important because you need to make sure that all the packages are listed in the requirements.txt file else you will have to wait for the build to fail and redeploy.

Make sure the server is running first on your local machine, remember the server will be set up from scratch but the database will already have applied migrations if you have applied migrations before after connecting the Heroku Postgres database.

python manage.py collectstatic

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

This will set up the origin of the remote repository that will be pushing the project code. Next, make sure to commit the code which will contain all the required stuff for deploying the code.

Checklist for deploying the code

- requirements.txt
- Procfile
- runtime.txt
- django-project
- environment variables / config variables 
- static file configuration
- database configuration
- migrate schema of database 
- gitignore file for ignoring virtualenvs, .env file, staticfiles, etc
Enter fullscreen mode Exit fullscreen mode

here's a sample .gitignore for a minimal django project.

.env/
.venv/
env/
venv/
*.env

*.pyc
db.sqlite3
staticfiles/
Enter fullscreen mode Exit fullscreen mode

If you want a full-fledged .gitignore for a complex django project, you can take the reference from Jose Padilla's gitignore Template for a django project.

Git Commit the Django Project

git status 

git add .

git commit -m "config for heroku deployment"
Enter fullscreen mode Exit fullscreen mode

Carefully check the status of the git repository before committing and make sure you don't forget anything by mistake, it won't a big problem but it would mess up the build process.

image.png

After committing the code, we can now push the code to GitHub. We first need to set the remote repository reference to be able to push the code to it.

git remote add origin https://github.com/<username>/<repo_name>
Enter fullscreen mode Exit fullscreen mode

This will set up the origin as the remote repository on GitHub. Once the remote repository is created, we can move into the push of the git repository that will trigger the build. First, navigate to the Deploy section in the heroku app's dashboard where we want to connect the GitHub repository and allow the automatic deploy from a branch in this case we have chosen the main branch.

Due to some Heroku Internal Server Issues, the GitHub integration seems to have broken and isn't working as of May 2022, but it might work later.

Below is a screenshot of my previous project deployed to Heroku using a GitHub repository.

image.png

git push origin main
Enter fullscreen mode Exit fullscreen mode

This will prompt you for your GitHub credentials and will deploy the commits to the remote repository on GitHub. This push on the main branch should also trigger the build process of the heroku app for this django project. You can navigate to the Activity section for the Build logs.

If you have followed the article well, and your repository has all the correct configurations, the build will succeed, else chances are you might have missed a few things and the app might have crashed. You can debug your application build with the simple heroku CLI command:

heroku logs --tail -a <app_name>
Enter fullscreen mode Exit fullscreen mode

This can be quite handy and saves a lot of time in understanding what went wrong in the build. It might be related to database migration, static files, python package not found, and some silly mistakes and errors that can be fixed after committing the code and pushing it to GitHub again.

If you do not want a GitHub repository, you can directly push the code from the local git repository to the remote heroku app center. This will require us the Heroku CLI.

Heroku CLI

We can use the heroku CLI for pushing the code via the simple git repository. We can push the references via the branch and a remote repository on heroku to build our app. For this, we assume you have heroku installed and logged in. We will require the django project code and heroku cli to build the django web application.

heroku git:remote -a <heroku_app_name>

# for my case
heroku git:remote -a blog-django-dep
Enter fullscreen mode Exit fullscreen mode

image.png

After this, you can commit your code and the project as git repository. We have added the remote repository location on heroku, we can now simply push the code to the remote repository.

git push heroku main
Enter fullscreen mode Exit fullscreen mode

image.png

Here, heroku is the remote repository location and main is the branch of the repository. This will push the code to the repository and thereafter create a build to deploy the Django project as a Heroku application.

image.png

You can hit the Open App button on the top right corner and there should be your deployed Django application.

image.png

A Few Tricks and Gotchas

There are a few tricks and issues that you might pop into while deploying a django project on heroku, especially the build process. It requires a few iterations to get the complete app setup.

Run command from the Dashboard console

If you don't have heroku CLI set up and want to fix a few things on the pushed code to the heroku app, you can use the Run Console option from the More Tab on the top right corner of theApplication dashboard. This is a great way to run migrations, configure static files or tweak a few things here and there without messing up the code on GitHub or the remote git repositories.

image.png

image.png

image.png

Deploy with Docker

You can even use the docker container to deploy a Django application on Heroku. It is a great way of learning a lot of deployment strategies and techniques using Docker. You'll get familiar with interesting concepts like virtualization, and containerization, and also learn Docker on the way. You can follow this tutorial on Deploying Django applications with Docker on Heroku. Also, you can check out the official Heroku documentation for deploying python applications.

What are Dynos?

Dynos are simply web processes or workers that serve your web application. Dynos in Heroku are allocated based on the build process, once the slug is created a dyno is created as it runs on a VM container. This simply means there are limitations on how to use the web application and its sleep process. The hobby tier is sufficient for normal testing projects and side projects though you will have to pay and move into advance tiers to increase the dyno allocations and scaling of those web processes.

It's not a simple thing to understand but to keep it simple, it might be a container to process the client's request and serve the page for a finite duration of the interaction. Also, your application will sleep after half an hour, if you try to reload the application every half an hour it will consume your resource allocation for the month and this is how the tiers and divided for paid services on Heroku. You can check out the detail over here.

Conclusion

So, that is one of the ways we can deploy a Django application on Heroku with the PostgreSQL database. You can find the [django-blog project] on [GitHub] for following along with the deployment process. In the next few parts of the series, we will be hopefully covering other platforms where you can deploy a Django o application.

Hopefully, you liked the above tutorial, if you have any questions. feedback, or queries, you can contact me on the Social handles provided below. Thank you for reading and till the next post Happy Coding :)

Top comments (5)

Collapse
osahenru profile image
Osahenru

Great stuff, what's the difference between deploying with a db like postgres and just deploying with Django's default db engine?

Collapse
mr_destructive profile image
Meet Rajesh Gor Author

There's just the difference of the database engine used, Its SQLite used in defualt django db, whereas Heroku provides a add-on to attach PostgreSQL db to the djang application.
They are quite similar but have their own use cases, SQLite is mostly used for embedded systems and mobile apps, whereas PostgreSQL is suitable for client server apps basically over a network.

Collapse
osahenru profile image
Osahenru

thanks for taking out time to explain

Collapse
zodman profile image
Andres 🐍 in 🇨🇦

for long road heroku is very expensive!

try dokku

Collapse
sm0ke profile image
Sm0ke • Edited on

Really nice.
In case you have time for aside projects, let me know.
Ty!

🌚 Browsing with dark mode makes you a better developer by a factor of exactly 40.

It's a scientific fact.