Browse files

Fixed #1321 -- Made DJANGO_SETTINGS_MODULE optional. You can now cal…

…l django.conf.settings.configure() to set settings manually if you don't have a settings module. Thanks, Malcolm Tredinnick, Luke Plant, Fredrik Lundh

git-svn-id: bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
adrianholovaty committed May 16, 2006
1 parent 27612d8 commit c643e12faf85478149386768d278c44936aa99a3
@@ -12,10 +12,61 @@
-class Settings:
+class LazySettings:
+ """
+ A lazy proxy for either global Django settings or a custom settings object.
+ The user can manually configure settings prior to using them. Otherwise,
+ Django uses the settings module pointed to by DJANGO_SETTINGS_MODULE.
+ """
+ def __init__(self):
+ # _target must be either None or something that supports attribute
+ # access (getattr, hasattr, etc).
+ self._target = None
+ def __getattr__(self, name):
+ if self._target is None:
+ self._import_settings()
+ if name == '__members__':
+ # Used to implement dir(obj), for example.
+ return self._target.get_all_members()
+ return getattr(self._target, name)
+ def __setattr__(self, name, value):
+ if name == '_target':
+ self.__dict__['_target'] = value
+ else:
+ setattr(self._target, name, value)
+ def _import_settings(self):
+ """
+ Load the settings module pointed to by the environment variable. This
+ is used the first time we need any settings at all, if the user has not
+ previously configured the settings manually.
+ """
+ try:
+ settings_module = os.environ[ENVIRONMENT_VARIABLE]
+ if not settings_module: # If it's set but is an empty string.
+ raise KeyError
+ except KeyError:
+ raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE
+ self._target = Settings(settings_module)
+ def configure(self, default_settings=global_settings, **options):
+ """
+ Called to manually configure the settings. The 'default_settings'
+ parameter sets where to retrieve any unspecified values from (its
+ argument must support attribute access (__getattr__)).
+ """
+ if self._target != None:
+ raise EnvironmentError, 'Settings already configured.'
+ holder = UserSettingsHolder(default_settings)
+ for name, value in options.items():
+ setattr(holder, name, value)
+ self._target = holder
+class Settings:
def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_settings):
if setting == setting.upper():
@@ -27,7 +78,7 @@ def __init__(self, settings_module):
mod = __import__(self.SETTINGS_MODULE, '', '', [''])
except ImportError, e:
- raise EnvironmentError, "Could not import settings '%s' (is it on sys.path?): %s" % (self.SETTINGS_MODULE, e)
+ raise EnvironmentError, "Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (self.SETTINGS_MODULE, e)
# Settings that should be converted into tuples if they're mistakenly entered
# as strings.
@@ -56,18 +107,32 @@ def __init__(self, settings_module):
# move the time zone info into os.environ
os.environ['TZ'] = self.TIME_ZONE
- settings_module = os.environ[ENVIRONMENT_VARIABLE]
- if not settings_module: # If it's set but is an empty string.
- raise KeyError
-except KeyError:
- raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE
+ def get_all_members(self):
+ return dir(self)
+class UserSettingsHolder:
+ """
+ Holder for user configured settings.
+ """
+ # SETTINGS_MODULE does not really make sense in the manually configured
+ # (standalone) case.
-# instantiate the configuration object
-settings = Settings(settings_module)
+ def __init__(self, default_settings):
+ """
+ Requests for configuration variables not in this class are satisfied
+ from the module specified in default_settings (if possible).
+ """
+ self.default_settings = default_settings
+ def __getattr__(self, name):
+ return getattr(self.default_settings, name)
+ def get_all_members(self):
+ return dir(self) + dir(self.default_settings)
+settings = LazySettings()
# install the translation machinery so that it is available
from django.utils import translation
@@ -327,14 +327,18 @@ def get_digit(value, arg):
-def date(value, arg=settings.DATE_FORMAT):
+def date(value, arg=None):
"Formats a date according to the given format"
from django.utils.dateformat import format
+ if arg is None:
+ arg = settings.DATE_FORMAT
return format(value, arg)
-def time(value, arg=settings.TIME_FORMAT):
+def time(value, arg=None):
"Formats a time according to the given format"
from django.utils.dateformat import time_format
+ if arg is None:
+ arg = settings.TIME_FORMAT
return time_format(value, arg)
def timesince(value):
@@ -435,7 +439,7 @@ def pprint(value):
return pformat(value)
except Exception, e:
return "Error in formatting:%s" % e
# Syntax: register.filter(name of filter, callback)
@@ -117,9 +117,12 @@ def translation(language):
globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale')
- parts = settings.SETTINGS_MODULE.split('.')
- project = __import__(parts[0], {}, {}, [])
- projectpath = os.path.join(os.path.dirname(project.__file__), 'locale')
+ if settings.SETTINGS_MODULE is not None:
+ parts = settings.SETTINGS_MODULE.split('.')
+ project = __import__(parts[0], {}, {}, [])
+ projectpath = os.path.join(os.path.dirname(project.__file__), 'locale')
+ else:
+ projectpath = None
def _fetch(lang, fallback=None):
@@ -155,7 +158,7 @@ def _merge(path):
if os.path.isdir(localepath):
res = _merge(localepath)
- if os.path.isdir(projectpath):
+ if projectpath and os.path.isdir(projectpath):
res = _merge(projectpath)
for appname in settings.INSTALLED_APPS:
@@ -540,6 +540,17 @@ you can override base translations in your project path. Or, you can just build
a big project out of several apps and put all translations into one big project
message file. The choice is yours.
+.. note::
+ If you're using manually configured settings, as described in the
+ `settings documentation`_, the ``locale`` directory in the project
+ directory will not be examined, since Django loses the ability to work out
+ the location of the project directory. (Django normally uses the location
+ of the settings file to determine this, and a settings file doesn't exist
+ if you're manually configuring your settings.)
+.. _settings documentation:
All message file repositories are structured the same way. They are:
* ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``
@@ -724,3 +724,70 @@ Django apps. Just follow these conventions:
* For settings that are sequences, use tuples instead of lists. This is
purely for performance.
* Don't reinvent an already-existing setting.
+Using settings without setting DJANGO_SETTINGS_MODULE
+In some cases, you might want to bypass the ``DJANGO_SETTINGS_MODULE``
+environment variable. For example, if you're using the template system by
+itself, you likely don't want to have to set up an environment variable
+pointing to a settings module.
+In these cases, you can configure Django's settings manually. Do this by
+calling ``django.conf.settings.configure()``.
+ from django.conf import settings
+ settings.configure(DEBUG=True, TEMPLATE_DEBUG=True,
+ TEMPLATE_DIRS=('/home/web-apps/myapp', '/home/web-apps/base'))
+Pass ``configure()`` as many keyword arguments as you'd like, with each keyword
+argument representing a setting and its value. Each argument name should be all
+uppercase, with the same name as the settings described above. If a particular
+setting is not passed to ``configure()`` and is needed at some later point,
+Django will use the default setting value.
+Custom default settings
+If you'd like default values to come from somewhere other than
+``django.conf.global_settings``, you can pass in a module or class that
+provides the default settings as the ``default_settings`` argument (or as the
+first positional argument) in the call to ``configure()``.
+In this example, default settings are taken from ``myapp_defaults``, and the
+``DEBUG`` setting is set to ``True``, regardless of its value in
+ from django.conf import settings
+ from myapp import myapp_defaults
+ settings.configure(default_settings=myapp_defaults, DEBUG=True)
+The following example, which uses ``myapp_defaults`` as a positional argument,
+is equivalent::
+ settings.configure(myapp_defaults, DEBUG = True)
+Either configure() or DJANGO_SETTINGS_MODULE is required
+If you're not setting the ``DJANGO_SETTINGS_MODULE`` environment variable, you
+*must* call ``configure()`` at some point before using any code that reads
+If you don't set ``DJANGO_SETTINGS_MODULE`` and don't call ``configure()``,
+Django will raise an ``EnvironmentError`` exception the first time a setting
+is accessed.
+If you set ``DJANGO_SETTINGS_MODULE``, access settings values somehow, *then*
+call ``configure()``, Django will raise an ``EnvironmentError`` saying settings
+have already been configured.
+Also, it's an error to call ``configure()`` more than once, or to call
+``configure()`` after any setting has been accessed.
+It boils down to this: Use exactly one of either ``configure()`` or
+``DJANGO_SETTINGS_MODULE``. Not both, and not neither.
@@ -7,6 +7,10 @@ perspective -- how it works and how to extend it. If you're just looking for
reference on the language syntax, see
`The Django template language: For template authors`_.
+If you're looking to use the Django template system as part of another
+application -- i.e., without the rest of the framework -- make sure to read
+the `configuration`_ section later in this document.
.. _`The Django template language: For template authors`:
@@ -876,3 +880,37 @@ The only new concept here is the ``self.nodelist.render(context)`` in
For more examples of complex rendering, see the source code for ``{% if %}``,
``{% for %}``, ``{% ifequal %}`` and ``{% ifchanged %}``. They live in
+.. _configuration:
+Configuring the template system in standalone mode
+.. note::
+ This section is only of interest to people trying to use the template
+ system as an output component in another application. If you are using the
+ template system as part of a Django application, nothing here applies to
+ you.
+Normally, Django will load all the configuration information it needs from its
+own default configuration file, combined with the settings in the module given
+in the ``DJANGO_SETTINGS_MODULE`` environment variable. But if you're using the
+template system independently of the rest of Django, the environment variable
+approach isn't very convenient, because you probably want to configure the
+template system in line with the rest of your application rather than dealing
+with settings files and pointing to them via environment variables.
+To solve this problem, you need to use the manual configuration option
+described in the `settings file`_ documentation. Simply import the appropriate
+pieces of the templating system and then, *before* you call any of the
+templating functions, call ``django.conf.settings.configure()`` with any
+settings you wish to specify. You might want to consider setting at least
+``TEMPLATE_DIRS`` (if you are going to use template loaders),
+``DEFAULT_CHARSET`` (although the default of ``utf-8`` is probably fine) and
+``TEMPLATE_DEBUG``. All available settings are described in the
+`settings documentation`_, and any setting starting with *TEMPLATE_*
+is of obvious interest.
+.. _settings file:
+.. _settings documentation:
@@ -507,4 +507,5 @@ def run_tests(verbosity=0, standalone=False):
raise Exception, msg
if __name__ == "__main__":
+ settings.configure()
run_tests(1, True)
@@ -73,6 +73,10 @@ def output(self, required_level, message):
def run_tests(self):
from django.conf import settings
+ # An empty access of the settings to force the default options to be
+ # installed prior to assigning to them.
# Manually set INSTALLED_APPS to point to the test models.
settings.INSTALLED_APPS = [MODEL_TESTS_DIR_NAME + '.' + a for a in get_test_models()]

0 comments on commit c643e12

Please sign in to comment.