I previously hosted two of my Django projects, NoteShare and Anime, on IBM Cloud. Now, I have decided to switch to Heroku, but the migration is not that straightforward. This article introduces how to deploy a Django project, created by PyCharm, on Heroku.
Why I Made This Switch
IBM Cloud, previously called IBM Bluemix, is a Platform-as-a-Service (PaaS) based on Cloud Foundry. It provides free Lite accounts, but the limitations of those free accounts are super inconvenient:
- After 10 days of no development activity, your apps go to sleep.
- After 30 days of no development activity, your service instances with Lite plans are deleted.
Having these rules means that I have to frequently update my projects even if I don’t need to. More interestingly, once an app goes to sleep, I must either push new code or log into the console to re-activate it. Therefore, I decided to switch to Heroku’s free plan. The free plan on Heroku also has the following limitation:
- If an app has a free web dyno, and that dyno receives no web traffic in a 30-minute period, it will sleep.
However, I can simply re-activate the app by visiting the URL of it. If you really want your app being online during most of the time in a day, you always have other options available. (By the way, it is not possible to make the free dynos always online.)
Differences Between IBM Cloud and Heroku
To me, there are two major differences.
- Cloud Foundry uses
.cfignore
to determine which files should be excluded from upload. This file can be different from.gitignore
which determines which files should be excluded from the Git repository. This gives me more freedom of maintaining files in a project. For example, I don’t want to ruin my repository by adding super large SQLite database file or numerous subtitle files of anime episodes. I don’t want to push the credentials of the project to GitHub, too, but I want them available in the cloud. To deal with these cases, I could simply include those files in.gitignore
but not in.cfignore
in the past. However, on Heroku I don’t have that level of control. - Heroku’s ephemeral filesystem makes it impossible to use file-based SQLite database, as I just explained that SQLite database will not and should not be added to the repository. Therefore, I must switch to some database systems such as Postgres or MySQL, and actually Heroku provides bunch of database add-ons. In addition, I cannot have any dynamically-generated files in the projects.
Therefore, I cannot directly push the projects to Heroku directly. Many changes must be made before doing that.
Setting Up a New Django Project Using PyCharm
Since there are lots of changes to my out-dated projects (which haven’t been maintained for a long time since my app on IBM Cloud was deleted) and I forgot the process of setting up Django projects, I decided to deploy a new one using PyCharm. The following tutorial assumes that:
- A Python environment for Django has been set up;
- Heroku CLI has been installed and configured;
- Postgres has been installed locally and a clean database has been created;
- A clean Django project has been created in PyCharm and the Git repository has been initialized;
- A Heroku app has been created.
Preparing the Project
We need at least three files in the top-level project folder.
runtime.txt
specifies the Python runtime the Heroku app uses. See a list of supported runtimes here.
Procfile
declares what command to execute to start the app. Since I uses Gunicorn as the server, here I just have one line:
1 | web: gunicorn django_site.wsgi --log-file - |
where django_site
is Django project’s name and --log-file -
means log to stderr
.
requirements.txt
defines app dependencies. It is recommended to run pip freeze > requirements.txt
to create it but at least those are necessary:
1 | Django>=3.0.0 |
Then run pip install -r requirements.txt
to install all the dependencies locally. I would maintain packages using conda
though.
In addition to those files, a .gitignore
is also necessary. It can be easily created on gitignore.io. You can also use mine.
Modifying Project’s settings.py
settings.py
is located inside django_site
folder. It is generated by PyCharm to configure the project. Here are the modifications:
Set SECRET_KEY
because it should not be exposed to the public. This is critical if the repository will be pushed to some public code bases such as GitHub. So first, mark down the secret key. Then run heroku config:set SECRET_KEY=<secret_key>
to set the environment variable SECRET_KEY
on Heroku. Also, add this environment variable to any PyCharm configurations. Finally, change the code to SECRET_KEY = os.environ.get('SECRET_KEY')
so that the app will read the key from the environment variable.
Set DEBUG = False
and DEBUG_PROPAGATE_EXCEPTIONS = True
.
Set ALLOWED_HOSTS
where django-site
is the name of the Heroku app:
1 | ALLOWED_HOSTS = [ |
Add middleware WhiteNoise by
- Adding
'whitenoise.runserver_nostatic'
toINSTALLED_APPS
; - Adding
'whitenoise.middleware.WhiteNoiseMiddleware'
toMIDDLEWARE
directly after the DjangoSecurityMiddleware
and before all other middleware; - Setting
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
1 | INSTALLED_APPS = [ |
Set database connection. In order not to expose database URLs in the code, first add import dj_database_url
to the very beginning of the file, then after DATABASES = { ... }
, add the following code:
1 | if 'DATABASE_URL' in os.environ: |
And don’t forget to add DATABASE_URL
environment variable to any PyCharm configurations with value postgresql://localhost:5432/django_site
if django_site
is the name of the local Postgres database.
Set static asset variables. For example:
1 | STATIC_URL = '/static/' |
Make sure that staticfiles
and static
folders exist in the top-level folder and static
folder is not empty (i.e., containing at least one file). Also run heroku config:set DEBUG_COLLECTSTATIC=1
to simplify debugging collectstatic on Heroku.
Set media file variables:
1 | MEDIA_URL = '/media/' |
Run python manage.py migrate
to apply migrations.
Setting Up a New Django Application
A Django project can have multiple Django applications. When PyCharm creates the project, an application can be created at the same time. The key things here are coding app/urls.py
, app/models,py
and app/views.py
, designing templates and adding static files to app/static/app
folder. Don’t forget to add the application to django_site/settings.py
and django_site/urls.py
.
When done, run the following commands to migrate schema changes and load initial data into the local database if the fixture is located at app/fixtures/app.json
:
1 | python manage.py makemigrations |
Pushing the Project to Heroku
We need to push both the local database and the local code repository to Heroku. I use Heroku Postgres add-on and have attached it to the Heroku app, so I just need to do:
1 | heroku git:remote -a django-site |
References
- Harman Deep Singh, “Deploying Django App on Heroku with Postgres as Backend,” Medium. [Online]. Available: https://medium.com/%40hdsingh13/b2f3194e8a43.