Search code examples
pythondjangodjango-settings

Django settings: raise KeyError, raise ImproperlyConfigured or use defaults?


Django expects you to use environment variables in settings.py to adapt to multiple environments (e.g. local, heroku, AWS).

I guess I should define -for instance- the username of the DB in an environment variable DB_USERNAME. How should I read it?

import os
DB_USERNAME = os.environ['DB_USERNAME']
DB_USERNAME = os.environ.get('DB_USERNAME')
DB_USERNAME = os.environ.get('DB_USERNAME', 'john')

Should I capture the KeyError and raise a ImproperlyConfigured myself? I prefer to make the app stop rather than run it using incorrect settings (defaults, people forgetting to set a variable,etc).

In the case of the defaults, it might even happen that john exists both loaclly and remotely, but with different permissions. It doesn't look very robust.


Solution

  • I would suggest a different approach to what you are doing.

    I use the following process:

    1. Create a .env or .ini file with your environment variables in it:

      DB_USERNAME=myDB
      A_NUMBER=1
      ALLOWED_HOSTS=localhost, 127.0.0.1, my_host
      DEBUG=False
      MY_DJANGO_KEY=no_peeking_this_is_secret
      
    2. Use decouple.config -which will make your life easier- to read you .env/.ini file:

      on settings.py:

      from decouple import Csv, config
      
      DEBUG = config('DEBUG', cast=bool, default=True)
      A_NUMBER = config('A_NUMBER') # No cast needed!
      ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=Csv(), default='')
      ...
      

    That will allow you to have multiple .env or .ini files for each of your possible configurations and you won't have to worry about mixing up permissions etc.

    Finally, I tend to use defaults with the lowest allowances possible (ex. look at my default allowed hosts which is an empty string).
    But if there exists some very important variable that must be set correctly, then:

    • The config() method will raise a UndefinedValueError if a variable is undefined and no defaults are set.

    • We can therefore create a try:... except: block to anticipate that:

      try:
          SUPER_IMPORTANT_VAR = config('SUPER_IMPORTANT_VAR')
      except UndefinedValueError:
          print('Hey!! You forgot to set SUPER_IMPORTANT_VAR!!')