Django Settings Flavours
Anyone who’s ever deployed a Django site will have encountered a common problem. You’ll usually have both a development and a production environment in which you’re running your Django project; many large-scale deployments will also have a staging environment. How do you maintain separate configurations for each of these environments, using each configuration in its appropriate environment?
In my Django projects, I have a setup, refined over a period of several months, which I feel provides a very neat, elegant and flexible solution to this problem. It leverages the fact that the default Django project layout, as generated by django-admin startproject
, assumes very little about the individual development process or deployment architecture which the project will have as it matures. While this can be intimidating for newcomers, it is a design decision which allows seasoned Djangonauts to stretch the boundaries of the framework, taking advantage of the power and flexibility provided.
Getting Started
I begin with the default project layout. Running django-admin startproject myproject
will give this:
myproject/
|-- __init__.py
|-- manage.py
|-- settings.py
`-- urls.py
I then break settings.py
into a directory, like so:
myproject/
|-- __init__.py
|-- manage.py
|-- settings
| |-- __init__.py
| |-- common.py
| |-- development.py
| `-- production.py
`-- urls.py
There are a few things to note about the settings
directory and its contents:
It contains an
__init__.py
file, which makes it a Python package.common.py
contains the settings which are common to all environments. This includes stuff likeROOT_URLCONF
,INSTALLED_APPS
,USE_I18N
, context processors, middleware and so on. It also includes a function called_merge()
, which I’ll get to later.There are modules for each deployment environment. These contain database settings,
DEBUG
andTEMPLATE_DEBUG
,CACHE_BACKEND
, et cetera.
In order to use a particular settings ‘flavour’, simply set the DJANGO_SETTINGS_MODULE
environment variable in the context where you’ll be running your application. For example, it might be myproject.settings.development
for your development environment, and myproject.settings.production
in production.
The _merge()
function
The only issue with this solution, as it stands, is how to get the settings from common.py
into the environment-specific modules. You could try from myproject.settings.common import *
, but that ties you into an absolute import. Instead, add the following function to the bottom of common.py
:
django-admin startproject myproject
And at the top of development.py
(or production.py
, et cetera):
myproject/
|-- __init__.py
|-- manage.py
|-- settings.py
`-- urls.py
This is essentially a workaround for the fact that relative imports cannot use import *
. When _merge()
calls globals()
, it gets the dictionary of the common.py
namespace, with each variable in the namespace being represented by a key => value
pair in the dictionary. At the module level in development.py
, vars()
returns this same dictionary, but for that module instead. Where it really helps is that both of these dictionaries support assignment; that is, vars()['foo'] = "bar"
is perfectly valid and will assign "bar"
to the variable foo
. Hence, _merge()
now has dictionaries for both the common.py
and development.py
namespaces, and it simply copies over all the public variables from common.py
(i.e. those not prefixed with an underscore) to development.py
.
It’s a concept best demonstrated by example. Let’s say common.py
contained the following:
settings.py
And development.py
contained this:
myproject/
|-- __init__.py
|-- manage.py
|-- settings
| |-- __init__.py
| |-- common.py
| |-- development.py
| `-- production.py
`-- urls.py
Running python -m settings.development
from the project root will print (1, 2, 3)
. The variables A
and B
have been copied over into the development.py
namespace for you to use as you wish.
Applying the Solution
All it takes is writing up your common.py
followed by your environment-specific modules, and you’re done. You can set the DJANGO_SETTINGS_MODULE
environment variable from an application.wsgi
file, a virtualenv activate
script, a web server configuration, or on the command line (if you’re using ./manage.py
).
For your information, I used the UNIX tree
command to generate the file listings for the example project.