start

As described in previous articles, you can also see some configuration references in Djangos configuration file Settings. Such as INSTALLED_APPS, MIDDLEWARE, and ROOT_URLCONF.

So how does this configuration module work?

start

This is generally recommended with reference to the Django configuration

from django.conf import settings
Copy the code

Rather than

from proj import settings
Copy the code

If you use the second form, Settings references other modules, which can cause circular references. In the first form, Django uses a lazy loading mechanism (loading only when needed).

Details can be viewed through the source code.

To understand


settings = LazySettings()

class LazySettings(LazyObject):
    def _setup(self, name=None):
        Extract the Settings module path from the environment variable
        settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
        self._wrapped = Settings(settings_module)

    def __getattr__(self, name):
        """ returns the set value and caches it in __dict__ """
        if self._wrapped is empty:
            self._setup(name)
        val = getattr(self._wrapped, name)
        self.__dict__[name] = val
        return val

    def __setattr__(self, name, value):
        if name == '_wrapped':
            self.__dict__.clear()
        else:
            self.__dict__.pop(name, None)
        super().__setattr__(name, value)

    ... # omit other functions
Copy the code

Lazy loading is when you need it. Django implements this mechanism through the proxy class LazyObject. The loading function is _setup, which is loaded when a property is acquired and cached in the instance’s __dict__.

LazySettings inherits From LazyObject and overrides __setattr__ and __getattr__, assuming that the __getattr__ method implementation is called when the settings.DEBUG property is called.

From there, we can observe that all attributes are retrieved from the _wrapped (Settings(settings_module) instance) private attribute.

Configuration is loaded

We saw above that the path to the Settings module is extracted from the environment variable, and the _wrapped attribute points to an instance of the Settings class.

class Settings:
    def __init__(self, settings_module):
        Read the default configuration
        for setting in dir(global_settings):
            if setting.isupper():
                setattr(self, setting, getattr(global_settings, setting))

        # Config module
        self.SETTINGS_MODULE = settings_module

        # dynamic import
        mod = importlib.import_module(self.SETTINGS_MODULE)

        tuple_settings = (
            "INSTALLED_APPS"."TEMPLATE_DIRS"."LOCALE_PATHS",
        )
        self._explicit_settings = set()
        # Read properties under config module (may override some default configurations)
        for setting in dir(mod):
            if setting.isupper():
                setting_value = getattr(mod, setting)
                setattr(self, setting, setting_value)
                self._explicit_settings.add(setting)
Copy the code

conclusion

If _wrapped is empty, the _setup method is called. This method gets the configuration file module internally. The _wrapped property points to an instance of the Settings class. When instantiated, the constructor reads global_settings to set some default properties, then loads the configuration module’s properties in the form of a dynamically imported module importlib.import_module, which reads the properties from _wrapped.