Django-Environ
Startup
manage.py
#!/usr/bin/env python import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv)
STARTUP
wsgi.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
DJANGO_SETTINGS_MODULE
Who exports?
Where?
When?
LOCAL settings
project/settings.py
DEBUG = False
SECRET_KEY = 'change-me'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
}
}
try:
from local_settings import *
except ImportError:
pass
LOCAL SETTINGS
project/local_settings.py
DEBUG = True
SECRET_KEY = 'my-secret-key'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase',
}
}
LOCAL SETTINGS
out of source control (.gitignore)
managed manually for environment-specific settings
hard to extend the base settings
project/local_settings.py
INSTALLED_APPS += ('debug_toolbar.apps.DebugToolbarConfig', ) # error
MIDDLEWARE_CLASSES += (
'django.contrib.sessions.middleware.SessionMiddleware',
) # error
LOCAL SETTINGS
ANTI-PATTERN!
Jacob Kaplan-Moss
THE twelve-factor APP
inspired by Martin Fowler’s books Patterns of Enterprise Application Architecture and Refactoring
Apps sometimes store config as constants in the code.
This is a violation of twelve-factor, which requires strict separation of config from code.
Config varies substantially across deploys, code does not.
-
declarative formats for setup automation
-
maximum portability between execution environments
-
suitable for deployment on modern cloud platforms
-
minimize divergence between development and production
-
scale up without significant changes to tooling
THE TWELVE-FACTOR APP
Configuration
-
config is everything that is likely to vary between deploys
-
strict separation of config from code
-
stores config in environment variables
Django settings module is a weird mix of:
application configuration
environment-specific configuration
environment-specific configuration
THE TWELVE-FACTOR APP
Backing Services
no distinction between local and third party services
THE TWELVE-FACTOR APP
Build, release, run
strict separation between the build, release, and run stages
The One True Way
Coined by Jacob Kaplan-Moss
reversing the import flow
import the base settings
from the environment-specific settings
project/settings/local.py
from .base import *
INSTALLED_APPS += ('debug_toolbar.apps.DebugToolbarConfig', )
MIDDLEWARE_CLASSES += (
'debug_toolbar.middleware.DebugToolbarMiddleware',
)
THE TWO-SCOOPS WAY
book written by Daniel Greenfeld and Audrey Roy
Best Practices for Django projects
-
using Virtualenvs
-
environment-related requirements and settings
- ...
In practice
Multiple settings modules and
Multiple requirements files
for each environment.
manage.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.development)
wsgi.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings.production")
run tests
python manage.py tests --settings=project.settings.tests
Read environment
The raw way
# project/settings/base.py
import os
DEBUG = bool(os.environ.get('DEBUG', False))
SECRET_KEY = os.environ['SECRET_KEY']
ALLOWED_HOSTS = os.environ['ALLOWED_HOSTS'].split()
Read environment
The semi-raw way
# project/settings/base.py
import os
from django.core.exceptions import ImproperlyConfigured
NOTSET = object()
def get_env_setting(setting, default=NOTSET):
try:
return os.environ[setting]
except KeyError:
if default is NOTSET:
raise ImproperlyConfigured("Set the %s env variable" % setting)
return default
DEBUG = bool(get_env_setting('DEBUG', Fals))
SECRET_KEY = get_env_setting('SECRET_KEY')
ALLOWED_HOSTS = get_env_setting('ALLOWED_HOSTS').split()
Read environment
Defaults
Casting
Validations
Errors Handling
Overrides
DJANGO-ENVIRON
defaults
import environ
env = environ.Env()
DEBUG = env.bool('DEBUG', default=False)
# or
env = environ.Env(DEBUG=(bool, False),)
DEBUG = env('DEBUG')
DJANGO-ENVIRON
VALIDATIONS & ERRORS HANDLING
import environ
env = environ.Env()
# Raises ImproperlyConfigured exception if SITE_ID is not defined in os.environ or if os.environ['SITE_ID'] contains not valid int.
SITE_ID = env.int('SITE_ID')
# Raises ImproperlyConfigured exception if SECRET_KEY not in os.environ
SECRET_KEY = env('SECRET_KEY')
DJANGO-ENVIRON
Casting
import environ
env = environ.Env()
DATABASES = {
# Raises ImproperlyConfigured if DATABASE_URL not in os.environ
'default': env.db(default='sqlite:////tmp/my-tmp-sqlite.db'),
}
dj-database-url
dj-search-url
dj-search-url
django-cache-url
....
LOAD ENVIRONMENT
virtualenv
postactivate/postdeactivate
export DEBUG=1
export SECRET_KEY=secure
supervisord
[program:my-project]
environment=DEBUG="1",SECRET_KEY="secure"
uwsgi
[uwsgi]
env=DEBUG=1
env=SECRET_KEY=secure
LOAD ENVIRONMENT
LOAD ENVIRONMENT
where?
manage.py / wsgi.py
or
settings/base.py
LINKS
:D
Django Environment
By Daniele Faraglia
Django Environment
- 1,951