Environment
settings with Django

Florian Demmer

django-environ

Django Meetup, 2017-11-07

The Twelve-Factor App

III. Config

Store config in the environment

https://12factor.net/config

A little history

  • django-admin startproject myproject
  • myproject/settings.py
  • from django.conf import settings
  • settings are at "myproject.settings"
     
  • create myproject/local_settings.py
  • import at end of myproject/settings.py
    → .gitignore!
    → multiple "locals" ... environments, stages?

Config per stage

  • make settings a package, add "stages":
    • myproject/settings/__init__.py
      from . base import *
    • myproject/settings/base.py
      → previously settings.py
    • myproject/settings/dev.py
      from . base import *
      → plus "local" settings

       
  • settings are at "myproject.settings"
  • settings are at "myproject.settings.dev"

Tell Django about stages

  • "myproject.settings" and "myproject.settings.dev"
  • using the environment!
     
  • in manage.py and wsgi.py:

    os.environ.setdefault(
      'DJANGO_SETTINGS_MODULE', 
      'myproject.settings'
    )
  • run:
    DJANGO_SETTINGS_MODULE=myproject.settings.dev ./manage.py runserver

More fine grained settings

  • eg. SECRET_KEY or passwords
     
  • export SECRET_KEY=foobar
    
    $ python3 -c "import os; print(os.environ['SECRET_KEY'])"
    
    foobar
    
    
  • works well with just strings

Introducing django-environ

  • $ export CACHE_URL=redis://localhost:6379/0
    
    $ python -c "
    import environ; 
    env=environ.Env(); 
    print(env.cache())
    "
    
    {
      'LOCATION': 'redis://localhost:6379/0', 
      'BACKEND': 'django_redis.cache.RedisCache'
    }
    

Custom env var and defaults

  • $ python -c "
    import environ; 
    env=environ.Env(); 
    print(
      env.cache(
        'MEMCACHED_URL',
        default='locmemcache://unique-snowflake')
    )"
    
    {
      'LOCATION': 'unique-snowflake', 
      'BACKEND': 'django...locmem.LocMemCache'
    }
    

More parameters

  • $ export DATABASE_URL=postgresql://app:password@localhost:5432/app?ATOMIC_REQUESTS=1 
    
    $ python -c "import environ; env=environ.Env(); print(env.db())"
    
    {'ENGINE': 'django...postgresql_psycopg2', 
    'HOST': 'localhost', 'ATOMIC_REQUESTS': True, 'USER': 'app', 'OPTIONS': {}, 'PASSWORD': 'password', 'PORT': 5432, 'NAME': 'app'}
    
    

Required settings

  • $ python -c "import environ; env=environ.Env(); print(env.db(NO_DATABASE_URL))"
    
    Traceback (most recent call last):
     File "<string>", line 1, in <module>
     File ".../django-environ/environ/environ.py", line 207, in db_url
        return self.db_url_config(self.get_value(var, default=default), engine=engine)
     File ".../django-environ/environ/environ.py", line 280, in get_value
        raise ImproperlyConfigured(error_msg)
    django.core.exceptions.ImproperlyConfigured: Set the DATABASE_URL environment variable

Datatypes

  • $ export BOOL_=1
    
    $ python -c "import environ; env=environ.Env(); print(env.bool('BOOL_'))"
    
    True
  • $ export FLOAT_=3,14
    
    $ python -c "import environ; env=environ.Env(); print(env.float('FLOAT_'))"
    
    3.14

Datatypes

  • $ export LIST_=a,b,c,d,1,2,3
    
    $ python -c "import environ; env=environ.Env(); print(env.list('LIST_'))"
    
    ['a', 'b', 'c', 'd', '1', '2', '3']
    
    
  • $ export DICT_=foo=bar,spam=42,ham=3.14
    
    $ python -c "import environ; env=environ.Env(); print(env.dict('DICT_'))"
    
    {'foo': 'bar', 'ham': '3.14', 'spam': '42'}

Managing environments

  • .bashrc or .profile
  • direnv (https://direnv.net)
     
  • let django-environ read a file
    • $ python -c "
      import environ; 
      env=environ.Env(); 
      environ.Env.read_env('.env')
      "
  • with docker-compose
    • ...

All the things

 

  • https://12factor.net/config

  • https://django-environ.readthedocs.io/en/latest/

  • https://github.com/fdemmer/django-environ

  • my twitter: @fdemmer

  • slides: https://slides.com/fdemmer

Django Meetup, 2017-11-07

By Florian Demmer

Django Meetup, 2017-11-07

  • 1,353