Package: django-local-settings
Local settings for Django projectsNew in version 2Settings can now be defined as environment variables (env settings)The values for env settings can be specified in a.envfile and
they'll be loaded automatically, or any standard method of setting
environment variables can be usedSecret settings can now have a callable default (they weren't allowed
to have a default before)List and dict settings can now have trailing commasLiteral date & time values can now be specified (2021-06-23, etc)Additional number syntaxes are now supported (any valid Pythonintorfloat)Support for Python 3.5 and below was dropped; supporting only 3.6 and
up is much simpler (officially, only 3.7 and up are supported, but
there shouldn't be any issues on 3.6 for the time being)Support for old, unsupported versions of Django was droppedThis package attempts to solve the problem of handling local settings in
Django projects. Local settings by definition can't be pre-defined,
although perhaps they can have a reasonable default value (mainly useful
for development). Another class of local settings aresecretsettings;
these definitely shouldn't be pre-defined and should never be added to
version control.The problems with local settings are:How to specify which settings are localHow to inform others which settings are local (or secret)How to actually give the local settings a valueHow to verify that local settings have been given a valid valueHow to ensure new local settings get setHow to ensure local (and esp. secret) settings don't get added to
version controlOne common approach is to create a local settings template module with
dummy/default values. When new developers start working on a project,
they copy this file (e.g.,local_settings.py.template => local_settings.py), which is typically excluded from version control.
This approach at least identifies which settings are local, but it's not
very convenient with regard to setting values and ensuring those values
are valid. Also, instead of giving you a friendly heads-up when you
forget to set a local setting, it barfs out an exception.This package takes the approach that there will be only one settings
module* per project in the standard location:{project}.settings. That
module defines/overrides Django's base settings in the usual wayplusit defines which settings are local and which are secret.In addition to the settings module, there will be one or more settingsfiles. These are standard INI files with the added twist that the
values are JSON encoded. The reasoning behind this is to use a simple,
standard config file format while still allowing for easy handling of
non-string settings.In addition, interpolation is supported using Django-style{{ ... }}syntax. This can be handy to avoid repetition.Once the local settings are defined,any missing settings will be
prompted for in the console(with pretty colors and readline support).In addition to settings files, settings can defined via environment
variables. These can be defined in a.envfile or using any other
mechanism for setting environment variables. When using a.envfile,
the values will be read in automatically; whennotusing a.envfile, the corresponding environment variables will need to be set prior
to loading the local settings.Env settings are typically strings like passwords and API tokens, but
they will proccessed like other settings--values will be loaded as JSON
and interpolated, etc.FeaturesLocal settings can be defined in a settings file (or files)oras
environment variables (AKA env settings)When using env settings, environment variables will be loaded from
a.envfile automatically, if presentMissing local settings will be prompted for (only when running on a
TTY/console)Local settings can be defined with validatorsLocal settings can be defined with doc stringsLocal settings can be nested in settings lists and dictsSettings files can extend from each otherSettings values can be injected into other settings values using a
special syntax (AKA interpolation, similar to the standard library'sconfigparser)Includes a script to easily generate local settings files for
different environmentsSupports Python 3.7 - 3.10Supports Django 2.2 - 3.2Basic usageIn your project's settingsmodule, import theinject_settingsfunction along with the types of settings you need:from local_settings import (
      inject_settings,
      LocalSetting,
      SecretSetting,
  )Then define some base settings and local settings:# project/settings.py
  from django.core.management import utils

  # This is used to demonstrate interpolation.
  PACKAGE = "local_settings"

  DEBUG = LocalSetting(default=False)

  # This setting will be loaded from the environment variable
  # API_TOKEN, which can be defined in a .env file or set directly
  # in the environment.
  SOME_SERVICE = {
      "api_token": EnvSetting("API_TOKEN"),
  }

  DATABASES = {
      "default": {
          "ENGINE": "django.db.backends.postgresql",
          "NAME": LocalSetting(default="{{ PACKAGE }}"),
          "USER": LocalSetting(""),
          "PASSWORD": SecretSetting(),
          "HOST": LocalSetting(""),
          "PORT": "",
      },
  }

  # If a secret setting specifies a default, it must be a callable
  # that generates the value; this discourages using the same
  # secret in different environments.
  SECRET_KEY = SecretSetting(
      default=utils.get_random_secret_key,
      doc="The secret key for doing secret stuff",
  )Local settings can be nested inside other settings. They can also
have doc strings, which are displayed when prompting, and default
values or value generators, which are used as suggestions when
prompting.This also demonstrates interpolation. TheDATABASES.default.NAMEsetting will be replaced with thePACKAGEsetting, so that its
default value is effectively'top_level_package'.Afterall the local settings are defined, add the following line:inject_settings()This initializes the local settings loader with the base settings
from the settingsmoduleand tells it which settings are local
settings. It then merges in the settings from the settingsfile.inject_settings()loads the project's local settings from a file
($CWD/local.cfgby default), prompting for any that are missing,
and/or environment variables, and returns a new dictionary with
local settings merged over any base settings. When not running on
a TTY/console, missing local settings will cause an exception to be
raised.Afterinject_settings()runs, you'll be able to access the local
settings in the settings module as usual, in case some dynamic
configuration is required. For example, you could doif DEBUG: .... At this point,DEBUGis no longer aLocalSettinginstance--it's a regular Pythonbool.Now you can run anymanage.pycommand, and you will be prompted to
enter any missing local settings. On the first run, the settings file
will be created. On subsequent runs when new local settings are added
to the settings module, the settings file will be appended to.Alternatively, you can run the includedmake-local-settingsscript
to generate a local settings file.Advanced usageTODO: Discuss using multiple settings files, extending a settings file
from another file, how to specify a settings file other than the default
oflocal.cfg, editing settings files directly, &c.Historical noteThe initial commit of this package was made on October 22, 2014, and the
first release was published toPyPIon March 11,
2015. At the time,  I didn't know about TOML. Otherwise, I probably
(maybe?) would have found a way to use TOML for Django settings.When I heard about TOML--I think related topyproject.tomlbecoming
a thing--I remember thinking it was quite similar to this package (or
vice versa), especially the splitting of dotted names into dictionaries
and the use of "rich" values rather than plain text.One of the biggest differences, besides this package being Django-
specific, is interpolation of both valuesandkeys, which I find
immensely useful (more for values, but occasionally for keys.)Another difference is that with TOML the config file section name
becomes part of the dictionary structure, whereas indjango-local-settingsit doesn't. In that regard,django-local-settingsis geared toward environments, such as
a development and production.One other difference is thatdjango-local-settingssupports loading
settings from environment variables out of the box.