So you’ve started your Django project. You’ve done some work on it, and all’s going well. You’re about to make your first deployment somewhere. You realise “Hey, I’m going to need different settings for each environment, what’s the best way to do that?”. It turns out people ask this a fair bit. I’ve read through a bunch of these, and none quite do it for me.
So here’s what I use - if it works for you, I’d love to hear it; if you think you can make it better, I’d love to know.
Splitting up the settings
First, some context. I want the side-effects of my changes to be minimal. I want to be able to easily tell which environment I’m in. I want switching environments to be painless. I want my changes to be no friction to someone else coming along to my code.
To that end, lets replace the settings module with a package:
$ ls -1 myproject/settings/ __init__.py base.py live.py staging.py local.py
We’ll migrate the contents of our original
settings/base.py, then have any configuration overrides for the live environment in
settings/live.py (and the equivalent for the staging environment) and any specific local overrides in
settings/local.py (we’ll also want to add this file to
.gitignore or similar).
Now, the bit where the magic happens -
import os from base import * ENVIRONMENT = os.getenv("DJANGO_ENVIRONMENT") if ENVIRONMENT == "live": from live import * elif ENVIRONMENT == "staging": from staging import * try: from local import * except ImportError: pass</pre>
Hopefully this should be fairly self-illustrative - in summary:
- Load in our core settings from base.py
- Grab the environment variable
DJANGO_ENVIRONMENTand store it in
- Load any environment-specific settings overrides
- If we have local overrides, pull those in
With just that, you’re ready to go. No further modifications necessary.
Why do this?
So why is this better than, say,
DJANGO_SETTINGS_MODULE or some random imports?
- Environment settings are clean. Want to add another environment? Just create a file of that name, and start putting settings in
- Easy to change environments for local developmen
- I use Heroku, (their free tier is awesome for small initial deployments) - unless I want to start hacking buildpacks, this seems the cleanest way
The final issue is how environment-specific settings should be populated. If you haven’t read The Twelve-Factor App, go and do so.
That said, I disagree slightly with some of their points about configuration - specifically, unsurprisingly, the dislike for environments. That said, I feel that the environments should be kept as minimal as possible, so as an example, here’s my
(UPDATE: Nope, they had a point about scaling, so now I use
AWS_STORAGE_BUCKET_NAME = PROJECT_NAME STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage' S3_URL = 'http://%s.s3.amazonaws.com/' % AWS_STORAGE_BUCKET_NAME STATIC_URL = S3_URL ADMIN_MEDIA_PREFIX = S3_URL + 'admin/'
This is nothing more than staticfiles configuration for storages using Amazon S3 and boto. You’ll note that I set nothing else in here - no
AWS_ACCESS_KEY_ID et cetera - all of these things come from environment variables. Static files is the main thing for me that is a function of environment.
Warning to Heroku users
Any Heroku users reading the above and planning on implementing it, just be warned - the current Heroku Python buildpack has the neat feature of autogenerating a
Procfile for you if it detects that you’re pushing a Django app. However, this detection is done by looking for a settings.py file, so if you implement what I describe above, you’ll need to create your own
Procfile - this is no arduous task, you shouldn’t be using the autogenerated one anyway, but this threw me a bit when I encountered it!