[Django]-Split configuration files using django-configurations

2👍

You could set a environment variable to specify which settings file to use, and then in yourmanage.py and wsgi.py, do something like this

config_mode = os.getenv('DJANGO_CONFIG_MODE', 'base')

config_dict = {
    'base': 'settings.base'
    'local': 'settings.local'
    'production': 'settings.production'
}

os.environ.setdefault('DJANGO_SETTINGS_MODULE', config_dict[config_mode])

# or if you prefer not using a dictionary or if-else blocks, you could
# set the settings file name you wish to use as the DJANGO_CONFIG_MODE environment
# variable and use that directly

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.{0}'.format(config_mode))

You might also want to specify the mode in your different settings files, so that your code can behave differently in different modes.

CONFIG_MODE = 'base'   # or 'local' or 'production'

in your various settings files, and use it in your code base as required.

👤elssar

4👍

The DJANGO_CONFIGURATION variable refers to a configurations.Configuration subclass. As described in the docs on usage patterns, the idea is to trade in the tedious overhead of maintaining multiple settings files for a much DRYer Mixin/Class workflow scheme. I know it’s annoying having to go change manage.py and wsgi.py, but you get a lot for it.

# example settings.py
from configurations import Configuration, values

# Build up some mixin classes for related or app-specific settings:
class AppsMixin(object):
    DJANGO_APPS = (
        'django.contrib.auth', 'django.contrib.contenttypes',
        'django.contrib.sessions', 'django.contrib.messages', 
        'django.contrib.staticfiles', 'django.contrib.sites',
        'django.contrib.flatpages', 'django.contrib.sitemaps',
        'django_extensions'
    )
    ADMIN_APPS = ('filebrowser', 'grappelli', 'django.contrib.admin',)
    DEV_APPS = ('django.contrib.admindocs', 'debug_toolbar',)
    DEFAULT_APPS = (
        'tagging', 'imagekit',
        'tinymce', 'ajax_select',
        'crispy_forms', #...
    )

    @property
    def INSTALLED_APPS(self):
        """ Control application ordering dynamically """
        OUT_APPS = self.DJANGO_APPS + self.ADMIN_APPS
        if self.DEBUG:
            OUT_APPS += self.DEV_APPS
        return  OUT_APPS + self.DEFAULT_APPS

class AuthURLMixin(object):
    LOGIN_REDIRECT_URL = 'site-login-success'
    LOGIN_URL = '/auth/login/'
    LOGOUT_URL = '/auth/logout/'

class CrispyFormsMixin(object):
    """ django-crispy-forms specific settings """
    CRISPY_TEMPLATE_PACK = 'bootstrap3'

    @property
    def CRISPY_FAIL_SILENTLY(self):
        return not self.DEBUG

class Base(AppsMixin, AuthURLMixin, CrispyFormsMixin, Configuration):
    """ Your equivalent for settings/base.py """
    pass

class Local(Base):
    """ ~ settings/local.py """
    DEBUG = True
    TEMPLATE_DEBUG = DEBUG
    # Custom setting that lets subclasses or your apps
    # check which subclass is active.
    STAGING = False
    # Enable a setting to be defined in os.environ, with a sensible default
    # Don't forget the 'DJANGO_' prefix (e.g. DJANGO_TIME_ZONE) 
    TIME_ZONE = values.Value('America/New_York')
    HTTPS_ONLY = False
    # Stash the secret key in os.environ as DJANGO_SECRET_KEY
    SECRET_KEY = values.SecretValue()

    @property
    def CSRF_COOKIE_SECURE(self):
        """ chained dynamic setting """
        return self.HTTPS_ONLY

    @property
    def SESSION_COOKIE_SECURE(self):
        """ chained dynamic setting """
        return self.HTTPS_ONLY

class Staging(Local):
    """ This class is used for testing before going production """
    STAGING = True 
    TIME_ZONE = values.Value('America/Phoenix')
    #...

class Prod(Staging):
    """ ~ settings/production.py """
    DEBUG = False
    STAGING = False
    HTTPS_ONLY = True

Then, from your laptop:

/path/to/project/$ python manage.py shell --configuration=Local
>>>from django.conf import settings
>>>settings.DEBUG, settings.STAGING, settings.TIME_ZONE
(True, False, 'America/New_York')    

From your remote server:

/path/to/remote/project/$ python manage.py shell --configuration=Staging
>>>from django.conf import settings
>>>settings.DEBUG, settings.STAGING, settings.TIME_ZONE
(True, True, 'America/Phoenix')

Once everything is perfect, go system-wide:

# /etc/environment
DJANGO_SETTINGS_MODULE=thisproject.settings
DJANGO_CONFIGURATION=Prod

Now the three-file problem has been solved with a few extras:

  • The dynamic properties allow you to toggle or interpolate any number
    of related settings at once through instance methods.
  • Chunks of settings can be introduced and removed through the mixins.
  • Sensitive key settings are kept out of source control.

So, you could conceivably make three of these files with three classes each but why not just make one with nine?

0👍

You could try using an single settings file and setting up conditions inside it. So when you run it locally it sets different parameters than when you run it remotely. For example:

import os

DEVELOPMENT_MODE = not os.path.isfile('/mnt/SERVER')

DEBUG = DEVELOPMENT_MODE
TEMPLATE_DEBUG = DEBUG

The above checks for a file on the machine ‘/mnt/SERVER’, my server has this file, my laptop does not. Its an empty file, just a place holder. But it sets a flag in my settings that I can use as such:

if DEVELOPMENT_MODE:
  CONST_URL = 'http://localhost:8000'
else:
  CONST_URL = 'http://www.website.com'

I’ve been using this for years, the advantage is my apache, wsgi, and manage.py remain untouched.

Leave a comment