Better Local Settings — January 5, 2010

A common convention I've seen in the Django community is to place code like the following at the bottom of your settings file: # ``settings.py`` # Lots of typical settings here. # ... then ... try: from local_settings ... [Read More]

Better Local Settings

A common convention I've seen in the Django community is to place code like the following at the bottom of your settings file:


# ``settings.py``
# Lots of typical settings here.
# ... then ...

try:
    from local_settings import *
except ImportError:
    pass

Then storing machine/user-specific settings within a local_settings.py> file. This file is set to be ignored by version control.

I can't argue with needing overridden settings (do it all the time) but that import is backward in my opinion. My preferred technique is to still use the settings.py and local_settings.py but relocate the import. So local_settings.py starts out with:


# ``local_settings.py``
from settings import * # Properly namespaced as needed.

# Custom settings go here.
DATABASE_HOST = 'localhost'

The only other change is passing --settings=local_settings to any ./manage.py or django-admin.py calls.

Trivial implementation, you still only need to override what you need, no changes needed to version control or development workflow. And since you've already imported all of the base settings, you can reuse that information to create new settings, like:


# ``local_settings.py``
from settings import *

DATABASE_NAME = "%s_dev" % DATABASE_NAME

This is by no means a new technique, nor did I come up with it, but it's been very useful to me and I have yet to encounter the drawbacks (other than specifying the --settings flag, which also has an even easier fix via DJANGO_SETTINGS_MODULE) of this approach.

Feedback, as always, is welcome.

10 Comments

Hi,

I normaly use the standard settings as my default settings. This will make things a bit easier with manage.py etc. But I don't do a normal `from local_settings import *`. My code looks like this:

try:
from local_settings import *
if 'apply_settings' in globals():
apply_settings(globals())
except ImportError:
pass

This allows me to have a function in local_settings.py that changes the settings of the original file inplace. The `apply_settings` function in local_settings.py would look like:

def apply_settings(settings):
settings['DATABASE_NAME'] = "%s_dev" % settings['DATABASE_NAME']

Ok I admit, its a bit hacky ... but it skips the `--settings=local_settings` part.
We have recently started using 3 files - default_settings, local_settings and settings. In settings.py all you need is:

# root settings conf. imports local settings and set DEBUG-related flags.
try:
from local_settings import *
except ImportError:
from default_settings import *

The first line of local_settings should be "from default_settings import *", then it can override those settings it sees fit.
I think that try: from local_settings ... is kind of ugly and inflexible.

I have used a very similar way described in this post for quite some time on a variety of different projects/servers, and it works great.

Instead of having a "local_settings.py" file I have a "settings_project.py" (which is included in VCS), and I then let the settings.py file hold the local settings, and exclude it from VCS.

Really simple, and I can access and override the project defaults as you show in your post, but without the need to specify --settings or DJANGO_SETTINGS_MODULE at all!

I just want to point out that this works in several levels if you wish. I have some projects that shares some settings, so I have different settings files on different "levels", and I can always override any setting in the local settings.py file.
I've recently started putting a "configs" directory in my Django projects. It contains subdirectories for each environment - configs/live, configs/staging, configs/devlaptop etc. Each of those directories contains a manage.py and settings.py file, and deployed configs also have an app.wsgi. The project itself has a common_settings.py file with all of the required settings for the application (INSTALLED_APPS, MIDDLEWARE_CLASSES etc). Then the environment settings.py files can import * from common_settings.py and add their own overrides.

The advantage of this approach is that all of my configs go in to source control, and I can run management commands for each environment just by changing in to that environments directory and using ./manage.py as normal. This has been working really well for me.
I fixed manage.py to try importing local_settings first: http://github.com/jbalogh/zamboni/blob/master/manage.py#L8

I'd been using the approach described in this post, with Andreas' modification so the final file is still settings.py. The problem I've had is that I want to modify or append to default settings (i.e. INSTALLED_APPS += ('debug_toolbar',)) - but I _also_ want to have derived settings (i.e. ADMIN_MEDIA_PREFIX = MEDIA_URL + 'admin/'), and be able to override the base setting and have the effect cascade (i.e. override MEDIA_URL in my local settings and have ADMIN_MEDIA_PREFIX change). The only way I was able to do this was to repeat the definition of ADMIN_MEDIA_PREFIX (and others) in my local settings, which gets ugly as I accumulate definitions in local settings that I'm not modifying directly.

Then I saw Transifex' solution here: http://code.djangoproject.com/wiki/SplitSettings#UsingalistofconffilesTransifex

Now I use that, and I find it perfect: much more flexible, and solves the above problem: I can have a 99_derived.conf file that sets all my "derivative" settings, and have a 95_local.conf before it that does my local overrides and has access to all the settings defined previously.
I use an approach similar to Simon. Our folder structure is:

project/settings/__init__.py # base config
project/settings/live.py
project/settings/testing.py
project/settings/dev.py
project/settings/local.py # excluded from vcs

Then using a bash script that uses an environment variable to switch to, it runs manage.py with an appropriate DJANGO_SETTINGS_MODULE. Works quite neatly.
I usually put in the end of the settings.py:

execfile(os.path.join(PROJECT_PATH, "settings_local.py"))

That's all.
That was inspiring,
My code usually looks like this,
Keep up the good work
There's a trick to avoid the subscript notation Gregor uses in the first comment. See "alternate more_settings.py" at: http://code.djangoproject.com/wiki/SplitSettings#Accessingandmodifyingexistingsettings

Sorry, comments have been closed on this post.