Corrections to Heroku Posts

Corrections

Some corrections have been made to my previous posts as a result of errors pointed out by instructors and classmates. The articles listed below have been updated to reflect the corrections.

Corrections to Lessons Learned from Deploying Django on Heroku:

1. Step 2: Corrections to the section on pip freeze > requirements.txt:

NEW VERSION

(CORRECTION): Contrary to both previous versions of this post, you can run pip freeze at any time. This will change requirements.txt by adding the new dependencies that have been installed.

If you edit requirements.txt manually, run pip install -r requirements.txt to install any new dependencies:

(venv)mainuser@webdevbox:~/hellodjango$ pip install -r requirements.txt

PREVIOUS VERSION

(CORRECTION): An earlier version of this post stated that the pip freeze command had to be run while the server was running. Actually, as long as the pip install django psycopg2 dj-database-url command succeeded, the new dependencies will be added to the file when you run pip freeze. DO NOT RUN pip freeze WHILE THE SERVER IS RUNNING.

2. Step 2: Corrections to the section on DATABASES in settings.py:

NEW VERSION

Next, the tutorial states “Next, configure the application for the Heroku environment, including Heroku’s Postgres database. The dj-database-url module will parse the values of the DATABASE_URL environment variable and convert them to something Django can understand.” Add these lines to the end of helloheroku/settings.py as specified in the tutorial:

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

(CORRECTION) An earlier version of this post stated that values in settings.py’s DATABASES section needed to be changed. This was unnecessary. The DATABASE_URL environment variable must be set with Linux’s export command. Thanks to Zach Tomaszewski for pointing this out.

(venv)mainuser@webdevbox:~/hellodjango$ export DATABASE_URL=postgres://testadmin:testing@localhost/django_db

Here, testadmin is a postgresql user, testing is the password for that user, and django_db is the name of the local database. On the local machine, dj_database_url.config() reads the value of DATABASE_URL specified by export and returns its value, which becomes the value of DATABASES. On Heroku, dj_database_url.config() will read the value of DATABASES from Heroku’s environment variables instead.

PREVIOUS VERSION

Next, the tutorial states “Next, configure the application for the Heroku environment, including Heroku’s Postgres database. The dj-database-url module will parse the values of the DATABASE_URL environment variable and convert them to something Django can understand.” Before this will work, the DATABASES section should be changed to match the database you created earlier:

(venv)mainuser@webdevbox:~/hellodjango$ nano hellodjango/settings.py
# Some of the file omitted
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'django_testdb',
        'USER': 'testadmin',
        'PASSWORD': 'testing',
        'HOST': '',
        'PORT': '',
    }
}
# Now go to the very end of the file and add these lines, as in the tutorial:

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

3. Corrections to Step 3:

Note: This section was accidentally titled Part 3.

NEW VERSION

If, like me, you had at first forgotten to configure PostgreSQL and do an export, you will see errors like this if you try to run the server locally:

[…]

If this happens, set up the database as described in Step 1 and edit settings.py as described in Step 2, then try again.

PREVIOUS VERSION

If, like me, you had at first forgotten to configure postgreSQL, you will see errors like this:

[…]

If this happens, set up the database as described in Step 1 and edit settings.py to configure the DATABASES values as described in Step 2, then try again.

4. Corrections to Step 4:

NEW VERSION

  1. In settings.py, change DEBUG to False. You should always do this before deploying a Django application with gunicorn, because gunicorn does not respect the DEBUG flag. In general, always change DEBUG to False before deploying any production Django application.
    DEBUG = False
    
  2. Add the line gunicorn==0.16.1 to your requirements.txt
  3. Change your Procfile’s web: line to web: gunicorn hellodjango.wsgi
  4. Uncorrected sections omitted…

PREVIOUS VERSION

  1. Add the line gunicorn==0.16.1 to your requirements.txt
  2. Change your Procfile’s web: line to web: gunicorn hellodjango.wsgi
  3. Uncorrected sections omitted…

Corrections to Lessons Learned from Deploying Django on Heroku, Part 2:

Corrections to Step 2:

NEW VERSION

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.

OLD VERSION

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

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle',
        'NAME': 'helloheroku_db',        # Or path to database file if using sqlite3.
        'USER': 'testadmin',             # Not used with sqlite3.
        'PASSWORD': 'testing',           # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

# Now go to 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.

Problems with Steps 3, 4, and 4A:

As it turns out, it’s supposed to be possible to get the site working with gunicorn as the server. As this would require substantial changes to Parts 3, 4, and 4A, I might just write a new article as an addition to this one, instead of trying to further revise the existing one.

In large part the problems with gunicorn and collectstatic stemmed from needing to run python manage.py collectstatic locally, and then to set DEBUG = False in settings.py.

One of our instructors, Yongwen Xu, gave this explanation:

6. “collectstatic” and “gunicorn” issues:
The static file handling is confusing both in Django and Heroku. One culprit is the flag “DEBUG” whose default value is set to True in settings.py. When the DEBUG flag is true, “manage.py runserver” will serve the static files automatically from the STATIC_ROOT and STATICFILES_DIRS settings, as defined in settings.py (“PROJECT_ROOT/site_media/static” and “PROJECT_ROOT/static”).
That is why students did not encounter this problem when using the “runserver” command locally and on Heroku.

The DEBUG setting is not honored by gunicorn, which is a server intended for production deployment (in which case the DEBUG flag should be set to false). So, when using gunicorn, the static files are NOT automatically served from STATIC_ROOT, thus, we don’t see any style in the app page, as in Jordan’s case.

One way we could improve the system is to add the following line to the urls.py, to explicitly tell the server to serve the static files from STATIC_ROOT:

urlpatterns += patterns('',
    (r'^' + settings.STATIC_URL[1:] + '(?P.*)$', 'django.views.static.serve',
         {'document_root': settings.STATIC_ROOT}), )

(Some text omitted…)

For local development, it’s best to always run “collectstatic”, so that the static files from both the app and the django admin are copied to the /site_media/static directory. Otherwise, even [if] the DEBUG flag is set to True, the style for the django admin interface will not be available because the admin static files are not in /site_media/static or /static dir.

On Heroku, the “collectstatic” is normally run by heroku automatically. see below on how to disable this:
https://devcenter.heroku.com/articles/django-assets

As a result of all this, for local development, “manage.py runserver” is easiest, while for production deployment, using “gunicorn” is recommended.

I will work on further revisions to the Part 2 post later, as time allows.

Advertisements