Irrational Exuberance!

A Different Approach to local_settings.py

November 28, 2008. Filed under django

A couple weeks ago when I spoke at Django NYC, in one of my slides I introduced the local_settings.py trick for Django, and on the next slide I mentioned a slight variant on the standard trick, which I've tried to introduce before, but the idea got lost in the midst of the longer article.

The Real local_settings.py

The original version of this trick is simple and extremely useful. At the bottom of your settings.py file, add these four lines

try:
    from local_settings import *
except ImportError:
    pass

Then you can create a custom local_settings.py file with overrides for the settings for your machine, and you're golden.

When you're creating public repositories, you make sure not to place your local_settings.py into the version control, but you can still create a local_settings.py.template file that others cp local_settings.py.template local_settings.py and then edit to their liking.

The Variant I Speak Of

The first time I used this variant I was working on an app which I deployed two places: on my VPS and on my personal machine. I wanted to be able to keep the settings for both in version control, but without any needing to make modifications each time I updated the checked out project.

My solution went like this:

  1. Have the settings.py file which contains all shared settings, and still ends with:

    try:
        from local_settings import *
    except ImportError:
        pass
    
  2. Store the settings for my development machine in devel_settings.py and store the settings for VPS in vps_settings.py.

  3. On each development, create a local_settings.py file that only contained one line. For my development server the line is:

    from devel_settings import *
    

    and for my VPS the line is:

    from vps_settings import *
    

Doing that, I can easily modify both setups and keep them in version control incase I want to deploy the project on another machine.

Essentially what's being done here is something akin to subclass settings files and overriding just what is necessary. You can get carried away with this stuff if you want to, and have a settings buffet where you combine pieces as you like.

For example, you could have a file named site_caching_settings.py that looks like this:

CACHE_BACKEND = "memcached://127.0.0.1:11211/"
CACHE_MIDDLEWARE_SECONDS = 60 * 30
CACHE_MIDDLEWARE_KEY_PREFIX = "scs"
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.cache.CacheMiddleware',
) +  MIDDLEWARE_CLASSES

and you could have a file named sqlite_settings.py:

ROOT_PATH = os.path.dirname(__file__)
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = os.path.join(ROOT_PATH, 'db.sqlite')
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''

Finally, in your local_settings.py you'd import the pieces you wanted (as well as override what you want to modify):

from site_caching_settings import *
from sqlite_settings import *
CACHE_BACKEND = "localmem:///"

You're Right. It doesn't scale.

I think the obvious weakness to this approach is that it doesn't scale well, and it isn't a replacement for the standard local_settings.py trick, but it has served me quite well for projects that only need to be deployed in a small number of environments. For example, work and personal projects that are not intended to be reusable or pluggable apps.