When you're developing a personal website, a blog, or a website for a small client, you typically don't spend a whole lot of time worrying about setting up dedicated development, staging, and production environments. When that small client becomes bigger, your personal website becomes a brand, or you accidentally release broken code, that's when it makes sense to start separating out those environments.
From a server standpoint, it's fairly straightforward on how to set up development and staging environments: You either buy new servers, set up virtual servers, or change the configuration on existing servers to deploy a different checkout of your Django project. Essentially whatever you did to deploy your project to production, you do that same process again to prepare the server for development and staging environments.
The more interesting question for our purposes is how to deal with these new environments from the Django side. The solution that I use is simple: an environment variable and some settings overrides files. Here's the code that I put at the bottom of my settings.py file to make it all work:
import sys import os FLAVOR = os.environ.get('FLAVOR', 'localdev') def override_settings(dottedpath): try: _m = __import__(dottedpath, fromlist=[None]) except ImportError: pass else: _thismodule = sys.modules[__name__] for _k in dir(_m): setattr(_thismodule, _k, getattr(_m, _k)) override_settings('settings_overrides.' + FLAVOR) override_settings('local_settings')
There's a lot packed into this small snippet of code. Firstly, the FLAVOR setting is what determines which type of environment we want to run. As you can see, it defaults to localdev which is for the case where you're developing on your local machine. You can set this environment variable to whatever you want. Typical values for FLAVOR include "dev", "staging", "prod", and "test".
The next part of this snippet is the override_settings function. It takes a dotted path to a settings file and imports everything in that file into the current settings file. If there are duplicate settings, the one from the override_settings function wins. As you can see, the settings from e.g. 'settings_overrides.staging' are imported, according to the current FLAVOR, and then finally local settings are imported if they are found.
As an example of how this all would work, here's how the directory structure could look:
settings.py
settings_overrides/
dev.py
staging.py
prod.py
test.py
Now that we have this setup, we can simply set this environment variable before running any manage.py commands:
FLAVOR=dev python manage.py syncdb
The above would run syncdb on the development database (the DATABASE_* options from settings_overrides/dev.py). You can also use the export command to set the environment variable for the rest of the terminal session:
export FLAVOR=dev python manage.py cleanup python manage.py syncdb
Also it's easy to set the environment variable using the os.environ dictionary, so if you're using mod_wsgi and Apache, you might have a production wsgi file that looked something like this:
import os import django.core.handlers.wsgi os.environ['FLAVOR'] = 'prod' os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' application = django.core.handlers.wsgi.WSGIHandler()
Using this technique is simple, straightforward, and an effective way of splitting out development, staging, and production environments for Django. It makes it easy to keep the settings isolated and cleanly separated. Watch out though, because sometimes it can be easy to forget what your FLAVOR environment variable is set to!
How do you manage development, staging, and production environments? Share your tips and tricks in the comments below.