Fixed #1321 -- Made DJANGO_SETTINGS_MODULE optional. You can now call 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: http://code.djangoproject.com/svn/django/trunk@2927 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2006-05-16 21:28:06 +00:00
parent 27612d8b7d
commit c643e12faf
8 changed files with 213 additions and 20 deletions

View File

@ -12,10 +12,61 @@ from django.conf import global_settings
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
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 @@ class Settings:
try:
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 @@ class Settings:
# move the time zone info into os.environ
os.environ['TZ'] = self.TIME_ZONE
# try to load DJANGO_SETTINGS_MODULE
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
def get_all_members(self):
return dir(self)
# instantiate the configuration object
settings = Settings(settings_module)
class UserSettingsHolder:
"""
Holder for user configured settings.
"""
# SETTINGS_MODULE does not really make sense in the manually configured
# (standalone) case.
SETTINGS_MODULE = None
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
translation.install()

View File

@ -327,14 +327,18 @@ def get_digit(value, arg):
# DATES #
###################
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)
register.filter(add)
register.filter(addslashes)

View File

@ -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 translation(language):
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:

View File

@ -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: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
All message file repositories are structured the same way. They are:
* ``$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)``

View File

@ -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()``.
Example::
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
``myapp_defaults``::
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
settings.
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.

View File

@ -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`: http://www.djangoproject.com/documentation/templates/
Basics
@ -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
``django/template/defaulttags.py``.
.. _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: http://www.djangoproject.com/documentation/settings/#using-settings-without-the-django-settings-module-environment-variable
.. _settings documentation: http://www.djangoproject.com/documentation/settings/

View File

@ -507,4 +507,5 @@ def run_tests(verbosity=0, standalone=False):
raise Exception, msg
if __name__ == "__main__":
settings.configure()
run_tests(1, True)

View File

@ -73,6 +73,10 @@ class TestRunner:
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.
settings.INSTALLED_APPS
# Manually set INSTALLED_APPS to point to the test models.
settings.INSTALLED_APPS = [MODEL_TESTS_DIR_NAME + '.' + a for a in get_test_models()]