Django Environmental Based Settings

Here's another small and easy Django tip, but one that I had a hard time finding an answer to when I first got started... I came from a Ruby on Rails and Groovy/Grails background before starting with Django. One thing both of those frameworks have is the concept of environments. Depending on what you're doing, you can have one of 3 environments going:

  • Development
  • Testing
  • Production

Typically the development environment was used when running the built-in server such as when you run ./manage.py runserver in Django. The testing environment was when running tests, of course. Similar to Django's ./manage.py test. Then the production environment was when running it on a real webserver such as Apache. You could set the environment if you felt like it as well, so you could run in development mode while under Apache. Each environment had its settings and different databases.

With a lot of my recent Django apps, I have noticed that I am duplicating code with the settings file. The typical way to run in different modes is to set the DJANGO_SETTINGS_MODULE environment variable before running. What I have noticed is common is to create 2 or 3 different settings.py files based on the different environments you want to set up. This is fine and all, but there are times when you may use similar settings between each file and all you really want to do is change what type of database you want to use depending on each environment.

For instance, when I'm doing development, I just want to use sqlite3. When running on Apache, I'd like it to use MySQL. However, everything else stays the same in my settings files because I don't really do any hardcoding of file paths or anything like that. If at any point I needed to change a setting that would be related to both development and production modes, I wouldn't want to change two different settings files to make those changes.

My solution to this problem was to set an environment variable and check it in the settings.py file. I called this environment variable DJANGO_RUN_ENV. I set this inside of my .wsgi file that is run when using Apache. If that environment variable was not set, I would default to the development mode. You could set this up in many different modes if needed, but I was only interested in production and development mode since when testing, the default is to use the in memory database. Here is what everything looks like...

Here is the top portion of my settings.py file:

import os
import django

RUN_ENV = 'DJANGO_RUN_ENV'

DEBUG = True
if os.genenv(RUN_ENV, '') == 'production':
    # We don't want debug in production
    DEBUG = False

SITE_ROOT = os.path.dirname(os.path.realpath(__file__))

if os.getenv(RUN_ENV, '') == 'production':
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'OPTIONS': {
                'read_default_file': os.path.join(SITE_ROOT, 'mysql.cnf')
            }
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': 'mydb.db',
        }
    }

# Further settings.py stuff

In my .wsgi file that is created to run Django under Apache (or any production webserver), I added these lines:

import os, sys
path = os.path.dirname(os.path.realpath(__file__))

if path not in sys.path:
    sys.path.append(path)

os.environ['DJANGO_RUN_ENV'] = 'production'
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

This can get a little messy if you let it. If you intend on changing a lot of stuff in the settings file, it may make more sense to have different settings files for each environment and then set the DJANGO_SETTINGS_MODULE environment variable appropriately. However, if at times you're like me and you just need to change one or two things in your settings file and having duplicated code in multiple settings files seems like a hassle, this may be a good option as well.

Feel free to comment...