DEV Community

loading...
Cover image for How to split Django settings for different environments?

How to split Django settings for different environments?

Valentine Solonechnyi
Full-stack web developer from Ukraine
・3 min read

So it's time to put your code into production. That means you have to connect some third party stuff, probably some external database or SMTP server. At the same time it would be nice to keep an option to modify and test your codebase with your local database and double-check on some other staging or QA server. But how to achieve this with default settings.py file?

Searching for the solution I found several approaches of splitting the Django settings, but one that worked for me appeared to be a mixture. Here's how I've got it to work with AWS EC2, but it should work with any python environment where you can set your own env variables.

Short recipe

  1. Django settings as a module.
  2. Key environmental variables for production and/or staging servers.
  3. __init__.py.

Directory view

your_main_django_app
-settings
    --__init__.py
    --base.py
    --local.py
    --production.py
    --staging.py

-__init__.py
-asgi.py
-urls.py
-views.py
-wsgi.py
Enter fullscreen mode Exit fullscreen mode

Step-by-step guide

1. Set environmental variable with unique key or use one.

I didn't set any env vars during development. But once I deployed my app to the staging server, it got a new env variable called 'RDS_DB_NAME'. I used it to differentiate between my local server and staging/production, but you can use your own unique key to perform the main check in the __init__.py file (i.e.'ENV_NAME' : 'Staging').

2. Prepare base.py

from pathlib import Path
import os

# The most important thing is to be build relative path
BASE_DIR = Path(__file__).resolve().parent.parent.parent

INSTALLED_APPS = []

#...other environmentally independent settings 
# (TEMPLATES, WSGI_APPLICATION, TIME_ZONE, STATIC_ROOT etc.)
Enter fullscreen mode Exit fullscreen mode

3. Add environment-specific settings

Since we have base.py, we can put the rest of the settings into their respective files: local.py, staging.py, production.py (you've got the idea).

By the way, don't forget to place your sensitive data (secrets, api keys etc.) into env variables instead of hard-coding them into your production.py. Here's the example of retrieving them in your settings:

'SECRET': os.environ.get('SECRET_KEY')

4. Tweak the import in init.py with if statement

Having all the settings in a separate module allows us not to specify local config directly each time we run manage.py runserver, because Django loads settings.py by default. And our settings module/folder is simply an equivalent to it.

Here's the init.py from the settings folder.

from .base import *
import os


if os.environ.get("ENV_NAME") == 'Production':
    from .production import *
elif os.environ.get("ENV_NAME") == 'Staging':
    from .staging import *
else:
    from .local import *
Enter fullscreen mode Exit fullscreen mode

Now, whenever your Django app is initialized on the server, your environment variable called "ENV_NAME" will determine the proper settings file. And if it's just your local dev server, no env variables will be accessible and your app will fall back to local.py settings.

*If you happened to use AWS Beanstalk for deploying python app, you might want to simplify your __init__.py to something shorter:

from .base import *
import os


if os.environ.get("RDS_DB_NAME") is None:
    from .local import *
Enter fullscreen mode Exit fullscreen mode

In this case, we're only telling the server to check one env variable and fallback to local.py. What about the production configuration then? Well, Beanstalk allows you to specify preferred settings in an environmental variable called DJANGO_SETTINGS_MODULE. Thus, you can just tune your Beanstalk app to load respective configuration like this:

DJANGO_SETTINGS_MODULE: "your_main_django_app.settings.production"

And that's all.

Everyone gets its own settings and your manage.py runserver works as before. Happy production.

This article was originally published in my blog post.

Discussion (0)