Lessons Learned from Deploying Django on Heroku, Part 2

NOTE: This article will set up a working Heroku application. However, there are issues with the way in which it handles collectstatic and gunicorn issues. For more information, see Corrections to Heroku Posts.

In a continuation of last week’s post, this post deploys a Github project on Heroku for a course assignment.

Heroku logo.

This blog is neither endorsed nor sponsored by Heroku.


SYSTEM DETAILS
Ubuntu 12.04.1 LTS (VirtualBox)

Prerequisites:

The prerequisites are the same as in my last article. Linux is recommended, since this guide uses its package manager. Additional steps may be required to install the equivalent software in Windows.

  • Heroku Toolbelt: $ sudo wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh
  • Git: $ sudo apt-get install git
    As far as I know, Git should be installed as part of the Heroku toolbelt. If not, install it manually.
  • PostgreSQL: $ sudo apt-get install postgresql
  • Python-Dev: $ sudo apt-get install python-dev
  • Virtualenv: $ sudo apt-get install virtualenv
  • A Heroku account. All services provided by Heroku will be free so long as the 750-hour monthly limit is not exceeded. Examine Heroku pricing here.
  • Other required packages (psycopg2, dj-database-url) are installed during the tutorial.

This article covers the process of deploying a Github project with Twitter Bootstrap features to Heroku.

Step 1: Import The Github Project

Create a new folder, cd to it, and import the Github project:

mainuser@webdevbox:~/workspace/$ mkdir HelloHeroku && cd HelloHeroku
mainuser@webdevbox:~/workspace/HelloHeroku$ git init
Initialized empty Git repository in /home/mainuser/workspace/HelloHeroku/.git/
mainuser@webdevbox:~/workspace/HelloHeroku$ git remote add origin https://github.com/cammoore/responsive-heroku.git
mainuser@webdevbox:~/workspace/HelloHeroku$ git pull origin master
remote: Counting objects: 1975, done.
remote: Compressing objects: 100% (1899/1899), done.
remote: Total 1975 (delta 69), reused 1975 (delta 69)
Receiving objects: 100% (1975/1975), 22.76 MiB | 695 KiB/s, done.
Resolving deltas: 100% (69/69), done.
From https://github.com/cammoore/responsive-heroku
 * branch            master     -> FETCH_HEAD
mainuser@webdevbox:~/workspace/HelloHeroku$

Next, initialize and source the virtual environment. Use pip to install dependencies:

mainuser@webdevbox:~/workspace/HelloHeroku$ virtualenv venv --distribute
mainuser@webdevbox:~/workspace/HelloHeroku$ source venv/bin/activate
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ sudo pip install -r requirements.txt
// Output omitted

Step 2: Create a New Postgresql Database

If you did not configure the pg_hba.conf file as described in Step 1 of the tutorial in the previous article, do so now.

You will need to create a database for the new application:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ sudo su postgres
(venv)postgres@webdevbox:/home/mainuser/workspace/HelloHeroku$ psql
psql (9.1.8)
Type "help" for help.

postgres=# CREATE DATABASE helloheroku2_db OWNER testadmin;
CREATE DATABASE
postgres=# \q
postgres@webdevbox:/home/mainuser/workspace/HelloHeroku$ exit
exit
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$

Now open helloheroku/settings.py in your text editor of choice and edit the following lines at the end of the file:

# Parse database configuration from $DATABASE_URL
import dj_database_url
DATABASES['default'] = dj_database_url.config()

# Honor the 'X-Forwarded-Proto' header for request.is_secure()
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

The file for the example project used here should already have the dj_database_url lines. The SECURE_PROXY_SSL_HEADER setting is used by Django to confirm that a request served from behind a proxy is being served as HTTPS.

(CORRECTION) Ignore the DATABASES section; dj_database_url.config() will import the values for DATABASES based on what is specified in the export command.

Now, export the database URL to your environment variables, and use set to check that it has been added. Note that you will need to re-export the environment variable every time you exit the terminal in which you started the virtual environment.

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ export DATABASE_URL=postgres://testadmin:testing@localhost/helloheroku_db
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ set | grep postgres
DATABASE_URL=postgres://testadmin:testing@localhost/helloheroku_db

Now add the changed file to Git:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ git add helloheroku/settings.py
// Output omitted
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ git commit -m "Configured settings.py to use postgresql database."
// output omitted

Step 3: Check or Configure Server Settings

The Github project used for this post is set to use Django’s own default web server by default, which runs with manage.py runserver:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ cat Procfile
web: python manage.py runserver 0.0.0.0:$PORT --noreload

NOTE: Though gunicorn was used in the last tutorial, it should not be used here.
Use pip to install the dependencies:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ pip install -r requirements.txt
// Output omitted

Step 4: Create Heroku Application

DISCLAIMER: If you plan to run the app locally, skip to Step 4A. For whatever reason, doing the python manage.py collectstatic manually in the local project before pushing it to Heroku may have caused my Heroku app to be unable to load the .less style files. I recommend that local and Heroku versions of this project be managed in separate directories to avoid this problem.

At this point, I was ready to create the Heroku application:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ heroku create
Creating still-dawn-7589... done, stack is cedar
http://still-dawn-7589.herokuapp.com/ | git@heroku.com:still-dawn-7589.git
Git remote heroku added

Now, push the source code to Heroku:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ git push heroku master
// Output omitted

The Twitter Bootstrap static files are automatically detected by Heroku, which runs a python manage.py collectstatic on them:

// Output omitted
       Successfully installed Django argparse distribute dj-database-url psycopg2
       Cleaning up...
-----> Collecting static files
// Output omitted

It is important not to run python manage.py collectstatic locally before creating the Heroku project, or the .less files used by Twitter Bootstrap may not work correctly. (I am not sure that this was the cause of the problem. For a longer explanation see the “Conclusions” section.)

The page without CSS styles.

Above: If you run a “python manage.py collectstatic” command before pushing to Heroku, styles might not be applied to the page. (The hidden-journey-6676 app has been deleted.)


Thanks to Justin Lee for pointing this out.

Now, sync the Heroku database and set up a superuser for Django:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ heroku run python manage.py syncdb
Running `python manage.py syncdb` attached to terminal... up, run.2022
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table django_admin_log
Creating table polls_poll
Creating table polls_choice

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'u15523'): helloherokuadmin
E-mail address: admin@example.com
Password: hhadmin
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$

Now, start the app and open it in your browser:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ heroku ps:scale web=1
Scaling web processes... done, now running 1
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ heroku open
Opening still-dawn-7589... done
(venv)mainuser@webdevbox:~/workspace/HelloHeroku$

If you see this error, continue:

Error message.

Above: Errors related to unresponsive .less scripts can usually be ignored. The page will eventually load.


The final page will look like this:
The Twitter Bootstrap-powered page.

Above: The final page.

Step 4A: (Optional) Run The App Locally

To run the example app locally, follow this post up until the end of Step 3. Then synchronize the database:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ python manage.py syncdb
// Output omitted. It is generally the same as the output from 
// the "heroku run python manage.py syncdb" command in Step 3.

Now run collectstatic:

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ python manage.py collectstatic

You have requested to collect static files at the destination
location as specified in your settings.

This will overwrite existing files!
Are you sure you want to do this?

Type 'yes' to continue, or 'no' to cancel: yes
// Output omitted...
Copying '/home/mainuser/workspace/HelloHeroku/static/js/prefixfree.min.js'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/Makefile'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/CONTRIBUTING.md'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/component.json'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/composer.json'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/package.json'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/README.md'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/LICENSE'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/js/bootstrap-popover.js'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/js/bootstrap-affix.js'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/js/bootstrap-transition.js'
Copying '/home/mainuser/workspace/HelloHeroku/static/twitter-bootstrap/js/bootstrap-scrollspy.js'
// Output omitted...
258 static files copied.

This will copy all the .less, .js, and other Twitter Bootstrap files to the site_media directory in the HelloHeroku directory.

Afterwards, start the local Django server with this command.

(venv)mainuser@webdevbox:~/workspace/HelloHeroku$ python manage.py runserver

Next, go to http://127.0.0.1:8000/ to view your page.

Conclusions

Deploying Github projects on Heroku seems to work fairly well as long as the default Django server is used. I am not sure whether my deployment problems were caused by the collectstatic command or by using gunicorn as the server; however, even when the collectstatic command was handled by Heroku, using gunicorn as the server caused the same problem. This suggests that the problem stems from using gunicorn as the web server. Otherwise, Heroku seems to be well-integrated with Git and allows you to manage your projects much as you would manage GitHub or a regular remote repository.

This post was last revised on February 27, 2013. See Corrections to Heroku Posts for more details.

Advertisements