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:
Have the
settings.pyfile which contains all shared settings, and still ends with:try: from local_settings import * except ImportError: pass
Store the settings for my development machine in
devel_settings.pyand store the settings for VPS invps_settings.py.On each development, create a
local_settings.pyfile 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.
There's one thing that's always bugged me about the local_settings.py trick. Say I want to add a middleware (SQL profiling middleware, for example) only on development. Because the local_settings don't have access to the global settings, in order to do this I have to do some hackery with some ADDITIONAL_MIDDLEWARE_CLASSES variable that my main settings.py knows about, or duplicate my entire middleware list. It's not really "inheritance" if I can't access what's above me in the inheritance tree.
What I've started doing instead is reversing the flow. I'll have a global_settings.py file in version control with all my standard settings, and then my settings.py (not in VC) will begin with "from global_settings import *". Then my localized settings can modify or tweak the global settings however they like.
I have recently reverted back from using the local_settings.py trick due to its lack of scalability and gone back to the manage.py --settings=foo way of doing it.
With this I have base_settings.py, and multiple *_settings.py files each for different tasks. Each custom settings file starts with the following:
from base_setting import *
and I then override from there.
I also have a settings.py file which always fails but prints a list of all *_settings.py files (except base_settings) which are available.
This has the benefit of being scalable at the expense of having to type more characters on the commandline.
The best solution is to use the environment for all configuration. Configuration should not be a part of the source code as it is. Create a ~/.django_env.sh to hold all of your config info. import os into your settings.py and use os.environ.get('DJANGO_DB','sqlite3') to configure everything. This is the most portable way of doing things as well as makes it a snap to move your app to a new server.
Regards,
David
I'm with David on this one. I use my normal
settings.pyfile as a development settings file then I have aproduction.settingsfile that gets used on the production boxes. In the case of the production settings, theyfrom settings import *and override as necessary. In mywsgifile I setDJANGO_SETTINGS_MODULE=project.production.settings. Additionally, I have two sub-domains that are branches of the master tree each with their own slightly modifiedproduction.settingsfile; thewsgifile for each of these sub-domains sets the proper env var.Basically, the entire process of choosing the right settings file is managed by the
wsgiconfig files so it's simply a matter of including them in Apache VirtualHost declarations and I'm done. On the dev box, if I don't want to use Apache and userunserverinstead, the development settings are used by default.Reply to this entry