Motivation
Recently, I was in the position to quickly prototype a location-based application. Since Django is pretty awesome for quick scaffolding with all other requirements still intact, I went straight for it using its extension GeoDjango1. The development process was pretty interesting 😌. However, deploying the prototyped application on Heroku was a huge pain 😢 since resources were sparse and those available were seemingly outdated.
This guide tends to provide an updated workthrough to overcome this hurdle.
Assumption
It is assumed that you have a GeoDjango1 application available for deployment. If not, go through 1 or Make a Location-Based Web App With Django and GeoDjango2. Another assumption is that you have chosen to host your application on Heroku and your database is PostgreSQL.
Content
Let's get started. Having configured your application for Heroku deployment3, append the following to your settings.py
file.
...
DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
If you follow Django Tutorial Part 11: Deploying Django to production3, it should come directly after:
# Heroku: Update database configuration from $DATABASE_URL.
import dj_database_url
db_from_env = dj_database_url.config(conn_max_age=500)
DATABASES['default'].update(db_from_env)
The added code instructs Heroku to use a spatial database extension called postgis. In most cases, failure to include that line results in this error:
AttributeError: 'DatabaseOperations' object has no attribute 'geo_db_type'
Next, we need to add a custom buildpack to our project on Heroku. By default, all Python applications are automatically given heroku/python
official buildpack. However, since our application needs more than just a python
runtime environment — our application needs GDAL
, GEOS
, and PROJ
in addition to a python runtime environment — we must get the additional environment in place for our application to compile. There are various buildpacks out there for this, however Heroku Buildpack Geo worked seamlessly.
NOTE: It should be noted that this buildpack only provides the runtime environments for GIS
related projects therefore, it cannot be used alone. You still need a Python runtime environment for your applications to run.
Doing this is pretty easy with Heroku since it supports multiple buildpacks.
To add this custom buildpack — after you created your heroku application using heroku create application_name
— follow the steps below:
-
Set the official python buildpack:
heroku buildpacks:set heroku/python
-
Insert the custom
heroku-geo-buildpack
:
heroku buildpacks:add --index 1 https://github.com/heroku/heroku-geo-buildpack.git
This will insert the
heroku-geo-buildpack
buildpack at the first position in the order of buildpack execution, and move the other buildpacks that are ahead of it down one position.4
NOTE: Ensure heroku-geo-buildpack
buildpack comes first! It installs the basic requirements of a GIS
application.
You can confirm it has been included by issuing the following command in your terminal:
heroku buildpacks
You can now happily deploy your application 😊㊗️🎈.
Did you run into some issues? The comment session is live 🤗.
Outro
Enjoyed this article? I'm a Software Engineer and Technical Writer actively seeking new opportunities, particularly in areas related to web security, finance, healthcare, and education. If you think my expertise aligns with your team's needs, let's chat! You can find me on LinkedIn and Twitter.
If you found this article valuable, consider sharing it with your network to help spread the knowledge!
Attribution
-
GeoDjango Tutorial - by Official Django Docs ↩
-
Make a Location-Based Web App With Django and GeoDjango - by Ahmed Bouchefra ↩
-
Django Tutorial Part 11: Deploying Django to production - by MDN ↩
-
Using Multiple Buildpacks for an App - by Heroku ↩
Top comments (20)
battling serious issues with deploying to heroku: dev-to-uploads.s3.amazonaws.com/up...
Worked locally?
yes
Did you put GDAL buildpack before python's? Did you configure your production database as specified in the post?
yes I did,
dev-to-uploads.s3.amazonaws.com/up...
is there a way we can have a google meet, i would be very grateful for this, i have spent the week debugging the issues
Before doing that, do you mind sending links to the screenshots of your
settings.py
? Ensure all sensitive data are properly hidden.Also, can you let me see the output of
heroku buildpacks
?thats the first link i sent: dev-to-uploads.s3.amazonaws.com/up...
from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.redis import RedisIntegration
from .base import *
import django_heroku
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))
SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config('DEBUG', '', cast=bool)
Application definition
Database
docs.djangoproject.com/en/2.2/ref/...
DATABASES = {
'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis',
}
}
------------------- HEROKU----------------------
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False)
django_heroku.settings(locals())
--------------------------SENDGRID-----------------------
EMAIL_BACKEND = "sendgrid_backend.SendgridBackend"
SENDGRID_API_KEY = config('SENDGRID_KEY', '')
SENDGRID_SANDBOX_MODE_IN_DEBUG = False
SENDGRID_TRACK_CLICKS_HTML = False
SENDGRID_TRACK_CLICKS_PLAIN = False
DEFAULT_FROM_EMAIL = 'Support Team support@intelconstruct.com'
----------------- SENTRY-------------------------
if not DEBUG:
sentry_sdk.init(
dsn="11add338ccc345c6a402cd8cb20d2bc1@o...",
integrations=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],
django_heroku.settings(locals())
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(weeks=521), # 10 years
"REFRESH_TOKEN_LIFETIME": timedelta(weeks=521),
}
Your stuff...
------------------------------------------------------------------------------
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
# Permission settings
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated'
],
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
DJSTRIPE_USE_NATIVE_JSONFIELD = True
these are required by Herokuuu
GEOS_LIBRARY_PATH = '/app/.heroku/vendor/lib/libgeos_c.so' if os.environ.get('ENV') == 'HEROKU' else os.getenv('GEOS_LIBRARY_PATH')
GDAL_LIBRARY_PATH = '/app/.heroku/vendor/lib/libgdal.so' if os.environ.get('ENV') == 'HEROKU' else os.getenv('GDAL_LIBRARY_PATH')
Your configurations don't conform with some of the details of this post. If you follow through, the following is not needed:
They are the prime suspects of your issues.
I added that when i discovered the issue, removing it still reproduces the issue. I am on Django 3.2.1 and Heroku 20
Did you add any buildpack aside the default one heroku gives?
no
GDAL can't work on heroku without you adding external buildpack to help you build Geospatial configurations. That is why I recommend you go through this article again and implement the stuff suggested. They are easy and not detrimental.
The error you are getting sterms from directing heroku to look for buildpacks and runtimes in arbitary paths that do not really exist.
i added this heroku buildpacks:add --index 1 github.com/heroku/heroku-geo-build...
according to your article
In your
settings.py
, add this to the bottom of the file:in my settings, i already added Database Engine and the error i am getting is related to the library gdal
What database are you using? And do you use heroku's dj_database_url?
Thank you very much. Followed the instructions and things went smoothly.