In this blog post, I will guide you through how to configure and deploy a Django project onto Heroku. Thus, we will go through the following steps:
- Setting up a Heroku app
- Configuring the local Django project
- Environment variables
- Static files storage
- Media files storage
- Database setup
- Adding extra deployment files to the project
- Push the project to GitHub
- Integrate and deploy the project on Heroku
Setting up a Heroku app
You can deploy a Django project on a lot of platforms - fx Google Cloud, AWS, and Digital Ocean. For my project, I have selected Heroku. Heroku is a Platform-as-a-service that enables developers to build, run, and operate applications entirely in the cloud. To do this, we have to first set up a (free) account here. After signing up, we can now log in and go to the Heroku dashboard overview of our apps here. In the upper right corner, you can here select "New" -> "Create new app". From here, you select the name of the app and click "create app". Once created, you can now click on the app in the dashboard overview and go to the app-specific section of Heroku. In the app overview, now go to the resources page. Here we need to set up two types of resources before we can proceed to the next step:
- Heroku Postgres: For setting up a database on the production server, we need to add the Heroku Postgres resource. Simply search for "Heroku Postgres" in the resource search bar and add the addon. This will set up a Postgres SQL database, which we can use for the storage of our model data.
- Cloudinary - Image and Video Management: To handle media storage (i.e. images that get uploaded when creating new blog posts), we need the Cloudinary addon. Simply search for "Cloudinary - Image and Video Management" in the resource search bar and add the addon. Once added, you will have to click on the addon and set up a Cloudinary account. With this, you can now see the Cloud Name, the API Key, and the API Secret of your new database on the Cloudinary interface. Copy-paste these to a local text editor for future reference.
We can now proceed to our local Django project.
Configuring the local Django project
In the local Django project, we need to make some adjustments to the settings.py file. First, we tackle the environment variables (i.e. the DJANGO_SECRET, DJANGO_DEBUG, and DJANGO_ALLOWED_HOSTS). For these, we need to hide the values of these variables before the code is pushed to GitHub (more on this later). So, we start by adding a new file to the same directory in which we find the settings.py file. We name this new file ".env". Inside this file, we now copy-paste our values:
DJANGO_SECRET_KEY=< your secret key >
DJANGO_DEBUG=False
DJANGO_ALLOWED_HOSTS=< yourhostname1,yourhostname2>
In the terminal, we now go ahead and install a package called django-environ (remember to activate your virtual environment, when working in your local Django project):
pip install django-environ
Back in the settings.py file, we now add/modify the following code:
import environ
env = environ.Env()
environ.Env.read_env()
(...)
SECRET_KEY = env('DJANGO_SECRET_KEY')
DEBUG = env('DJANGO_DEBUG')
ALLOWED_HOSTS = tuple(env.list('DJANGO_ALLOWED_HOSTS'))
Then we jump back to our Heroku app interface. Here we go to the settings tab and scroll to the Config Vars section. Here we now add the three environment variables (DJANGO_SECRET_KEY, DJANGO_DEBUG, and DJANGO_ALLOWED_HOSTS) from our .env file.
Now, we need to fix the storage of our static files. In production, we cannot rely on Django's build storage of static files. Instead, we here use the Whitenoise package which we install locally with the following command in the terminal:
pip install whitenoise
In the settings.py file, we now add the following line in the middleware list (just after the Django SecurityMiddleware):
MIDDLEWARE = [
#
'whitenoise.middleware.WhiteNoiseMiddleware',
#
]
Further down in the settings.py file, we now add the following (in the # Static Files section):
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = 'static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATICFILES_STORAGE = 'whitenoise.storage.CompressedStaticFilesStorage'
We can now proceed to the media files storage. For this, we need to use the Cloudinary addon we configured earlier for our Heroku app. Go to the terminal and install the following packages with this command:
pip install cloudinary django-cloudinary-storage Pillow
Then in the static files section of the settings.py file, where we just copied our code for static files, we now add the following lines of code for how the project shall handle media files storage:
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
CLOUDINARY_STORAGE = {
'CLOUD_NAME': env('C_CLOUDNAME') ,
'API_KEY': env('C_APIKEY') ,
'API_SECRET': env('C_APISECRET') ,
}
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
As you can see, we are now referencing three new variables in our .env file. Thus, we configure these variables. Go to the .env file and copy-paste the variables for your database from the Cloudinary interface:
C_CLOUDNAME=<cloudname>
C_APIKEY=<apikey>
C_APISECRET=<apisecret>
You now have to go to your Heroku app settings tab and configure these variables as well in the Config Vars section. Once done, head back to the settings.py file and add the following lines of code:
import cloudinary
import cloudinary_storage
(...)
INSTALLED_APPS = [
(...)
# Media Cloudinary
'cloudinary',
'cloudinary_storage',
]
To finalize the configuration of static and media storage, we now need to create two folders in the root directory of our project: one called "staticfiles" and one called "media". Then we head to the urls.py file and add the following lines of code at the bottom:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Finally, we now go to the terminal line collect all static files:
python manage.py collectstatic
We can now go on to configuring the database setup for our project. For this, we have to go back to the settings.py file. Here, we now go the database section, where we add the following lines of code after the DATABASES dictionary:
import dj_database_url
db_from_env = dj_database_url.config()
DATABASES['default'].update(db_from_env)
Then we head back to the terminal to install the required packages:
pip install dj-database-url
pip install psycopg2-binary psycopg2 django-heroku
Finally, we then place the following code at the very end of the settings.py file:
# Configure Django App for Heroku.
import django_heroku
django_heroku.settings(locals())
We have now configured our local Django project for deployment. The next step is to add some extra deployment files to the project.
Adding extra deployment files to the project
For the Django project to be successfully deployed to Heroku, we need to add some extra files and packages. We start by installing the required package:
pip install gunicorn
Then we create three new files in the root directory of our project:
- requirements.txt: In here, we need to add all dependencies our local project has (i.e. a list of all packages installed in our virtual environment). This is as easy as running the following command in the terminal:
-
pip freeze > requirements.txt
-
- runtime.txt: this file specifies for Heroku what version you want the project to run on on the server. In my case, it is
-
python-3.9.0
-
- Procfile: this is a Heroku-specific file, in which we specify the processes our application should run. In my case, this is the content of my Procfile:
-
release: python manage.py migrate web: gunicorn blog.wsgi --log-file -
-
With these three files configured, we can now begin the process of pushing the project to GitHub.
Push the project to GitHub
The reasons we want to push our project to the version control platform, GitHub, are that:
- we want to always know exactly what version of our project, we are deploying on Heroku. On GitHub, we will always have access to the most updated, deployed version plus we have the project stored in the cloud instead of locally, thus we can always access it from whatever device, we work.
- Heroku and GitHub are well-integrated. Thus, the deployment process can be automated, whenever we push a new, updated version of our project to GitHub (more on this later).
Before we push our project to GitHub, we need to tell Git what files to exclude from the upload. This is particularly important, as we do not want to publish any secret keys or passwords to the public or to upload files that are not important for the deployment on Heroku. To do this exclusion of files in the push to GitHub, we set up a new file in the root directory called ".gitignore". In this, we define all the names of the files, we want to ignore in our push to GitHub. You can see the configuration of my .gitignore file here. Once, this file has been set up, we can proceed to upload the project onto GitHub.
To host the project on GitHub, we need to set up a so-called repository for the project to be stored in. To do this, you have to set up a GitHub account here. Once logged in, you can now go to your repository overview and add a new repository (naming it in the process). After the setup of your new repository, you are presented with a page on GitHub, with some different commands you can use to start working with your repository. One of these commands is this:
git remote add origin <your repo url.git>
Copy this command, as we will be using it locally. Now, you jump to your local project directory. In the terminal, you can now run the following commands to initialize, add, and commit your local project:
git init
git add .
git commit -m '<your commit message'>
Then we run the git remote command presented on the repository page:
git remote add origin <your repo url.git>
Before we push the project to the GitHub repository with the following, final command:
git push -u origin master
Now you can go back to your GitHub and refresh the page of your repository. You should be able to see the files of your local project excl. the files specified in the .gitignore file. With this done, we just need to set up our project for deployment on Heroku.
Integrate and deploy the project on Heroku
We have now reached the final step: deploying our project with Heroku. To do this, we need to log in to our Heroku account and find the app, we have previously set up for this purpose. Here, we now need to integrate Heroku to our GitHub repository. Go to the Deploy tab in the Heroku app interface. In here, go to the Deployment method section and select GitHub. If this is the first time you connect your Heroku account to your GitHub account, you need to authenticate with GitHub - this is a fairly easy process. After authentication, you can now write in the name of your GitHub repository and select the correct one from the list provided by Heroku. Now, your GitHub repository and Heroku app are connected. Finally, you can scroll further down the deploy page on Heroku to find the "Manual Deploy" section. Write the name of your branch ("master" in this case) and click "Deploy Branch". Heroku will now begin the process of deploying your app based on the GitHub repository and branch specified. Head back to the Heroku app overview tab and wait for the deployment to be completed. You can now click on the "Open app" button on the upper right of the Heroku app interface and a new tab will open with your production website. If you want to automatically deploy your Heroku app each time you make a new push of your project to the GitHub repository, you can go back to the Deploy tab and click the "Enable Automatic Deploys" button. Then go back to your local project and adjust a file or two, before you run the following commands:
git add .
git commit -m '<your new commit message'>
git push -u origin master
Your GitHub repository should now reflect the changes, you have pushed and thus the Heroku app should run a new deployment. And with this, we have now gone through how to deploy a local Django project to Heroku.