Skip to content

django environ

Installation

Bash
pip install django-environ

Splitting the Settings File

Particularly useful when deploying in production, split settings are a way to partition Django settings depending on the environment. In a typical project, you may have:

  • The base environment, common for all scenarios
  • The development environment, with settings for development
  • The test environment, with settings that apply only to testing
  • The staging environment
  • The production environment

The theory is that depending on the environment, Django loads its settings from a .env file. This approach is known as the Twelve-Factor app, first popularized by Heroku in 2011.

There are many libraries for Twelve-Factor in Django. Some developers prefer to use os.environ to avoid additional dependencies altogether. My favorite library is django-environ.

For our project we set up three environments: base, development, and later production.


Once the folder is in place, create another file for the base environment in decoupled_dj/settings/base.py. In this file, we import django-environ, and we place everything Django needs to run, regardless of the specific environment. Among these settings are:

Abstract

  • SECRET_KEY
  • DEBUG
  • INSTALLED_APPS
  • MIDDLEWARE
  • AUTH_USER_MODEL

Remember that in the previous section we configured a custom Django user. In the base settings we need to include the custom user app in INSTALLED_APPS, and most important, configure AUTH_USER_MODEL. Our base settings file like this:

Base Settings for Our Project

settings/base.py
import environ
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env()
environ.Env.read_env()
SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", False)

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "users.apps.UsersConfig",
]

MIDDLEWARE = [ # OMITTED FOR BREVITY ]
ROOT_URLCONF = "decoupled_dj.urls"
TEMPLATES = [ # OMITTED FOR BREVITY ]
WSGI_APPLICATION = "decoupled_dj.wsgi.application

DATABASES = {"default": env.db()
AUTH_PASSWORD_VALIDATORS = [ # OMITTED FOR BREVITY ]
LANGUAGE_CODE = "en-GB"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = Tru
STATIC_URL = env("STATIC_URL")
AUTH_USER_MODEL = "users.User"

Note I have omitted for brevity the complete code for the following configurations: MIDDLEWARE, TEMPLATES, and AUTH_PASSWORD_VALIDATORS. These should have the default values that come from stock Django.

Next up we create an .env file in the decoupled_dj/settings folder. This file will have different values depending on the environment. For development we use the values in Listing.

Environment File for Development

.env
DEBUG=yes
SECRET_KEY=!changethis!
DATABASE_URL=psql://decoupleddjango:localpassword@127.0.0.1/decoupleddjango
STATIC_URL=/static/

If you want to use SQLite in place of Postgres, change DATABASE_URL to:

DATABASE_URL=sqlite:/decoupleddjango.sqlite3

To complete the setup, create a new file called decoupled_dj/settings/development.py and import everything from the base settings. In addition, we also customize the configuration. Here we are going to enable django-extensions, a handy library for Django in development

The Settings File for Development

settings/development.py
# noqa
from .base import *
INSTALLED_APPS = INSTALLED_APPS + ["django_extensions"]

Let’s also install the library:

pip install django-extensions

Use ALLOWED_HOSTS in .env

settings.py
# settings.py

import environ

# Initialize environment variables
env = environ.Env()
environ.Env.read_env()

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool('DEBUG', default=False)

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY', default='your-default-secret-key')

# Define ALLOWED_HOSTS based on DEBUG
if DEBUG:
    ALLOWED_HOSTS = ['*']  # Allow all hosts during development
else:
    ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['.example.com'])

# Other settings...

.env

.env
DEBUG=False
SECRET_KEY=your-production-secret-key
ALLOWED_HOSTS=.example.com,.anotherdomain.com

Reference