DEV Community

Cover image for Deploying GeoDjango applications on heroku: Easy workthrough
John Owolabi Idogun
John Owolabi Idogun

Posted on • Updated on

Deploying GeoDjango applications on heroku: Easy workthrough

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'
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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


  1. GeoDjango Tutorial - by Official Django Docs 

  2. Make a Location-Based Web App With Django and GeoDjango - by Ahmed Bouchefra 

  3. Django Tutorial Part 11: Deploying Django to production - by MDN 

  4. Using Multiple Buildpacks for an App - by Heroku 

Top comments (20)

Collapse
 
mojemoron profile image
Micheal Ojemoron • Edited

battling serious issues with deploying to heroku: dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
sirneij profile image
John Owolabi Idogun

Worked locally?

Collapse
 
mojemoron profile image
Micheal Ojemoron

yes

Thread Thread
 
sirneij profile image
John Owolabi Idogun

Did you put GDAL buildpack before python's? Did you configure your production database as specified in the post?

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

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

Thread Thread
 
sirneij profile image
John Owolabi Idogun

Before doing that, do you mind sending links to the screenshots of your settings.py? Ensure all sensitive data are properly hidden.

Thread Thread
 
sirneij profile image
John Owolabi Idogun

Also, can you let me see the output of heroku buildpacks?

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

thats the first link i sent: dev-to-uploads.s3.amazonaws.com/up...

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

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()],

    # If you wish to associate users to errors (assuming you are using
    # django.contrib.auth) you may enable sending PII data.
    send_default_pii=True
)
Enter fullscreen mode Exit fullscreen mode

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')

Thread Thread
 
sirneij profile image
John Owolabi Idogun

Your configurations don't conform with some of the details of this post. If you follow through, the following is not needed:

...
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')
Enter fullscreen mode Exit fullscreen mode

They are the prime suspects of your issues.

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

I added that when i discovered the issue, removing it still reproduces the issue. I am on Django 3.2.1 and Heroku 20

Thread Thread
 
sirneij profile image
John Owolabi Idogun

Did you add any buildpack aside the default one heroku gives?

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

no

Thread Thread
 
sirneij profile image
John Owolabi Idogun

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.

Thread Thread
 
sirneij profile image
John Owolabi Idogun • Edited

The error you are getting sterms from directing heroku to look for buildpacks and runtimes in arbitary paths that do not really exist.

Thread Thread
 
mojemoron profile image
Micheal Ojemoron

i added this heroku buildpacks:add --index 1 github.com/heroku/heroku-geo-build...
according to your article

Thread Thread
 
sirneij profile image
John Owolabi Idogun

In your settings.py, add this to the bottom of the file:

DATABASES['default']['ENGINE'] = 'django.contrib.gis.db.backends.postgis'
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
mojemoron profile image
Micheal Ojemoron

in my settings, i already added Database Engine and the error i am getting is related to the library gdal

Thread Thread
 
sirneij profile image
John Owolabi Idogun • Edited

What database are you using? And do you use heroku's dj_database_url?

Collapse
 
ecngjohn10011 profile image
Daniel John

Thank you very much. Followed the instructions and things went smoothly.