Skip to main content

Dynamic Settings Management

In this project, settings are managed through a lazy-loading system that allows for both environment-based configuration and manual setup for standalone scripts.

Accessing Project Settings

To access settings anywhere in your code, import the settings object from django.conf. This object is an instance of LazySettings, which defers loading until the first time an attribute is accessed.

from django.conf import settings

# Accessing a standard setting
if settings.DEBUG:
print("Debug mode is on")

# Accessing a list-based setting
for app in settings.INSTALLED_APPS:
print(f"Loaded app: {app}")

The LazySettings proxy ensures that settings are only loaded when needed. If the settings have not been configured yet (either via environment variables or manually), accessing an attribute will trigger the _setup() method.

Configuring Settings for Standalone Scripts

When writing standalone scripts that use Django components (like the ORM or management commands) without a full web server environment, you must manually configure the settings before calling django.setup().

Use settings.configure() to provide the necessary configuration. This creates a UserSettingsHolder to manage your manual overrides.

import django
from django.conf import settings

def run_standalone_task():
# Manually configure settings
settings.configure(
DEBUG=True,
DATABASES={
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'db.sqlite3',
}
},
INSTALLED_APPS=[
'django.contrib.contenttypes',
'django.contrib.auth',
],
SECRET_KEY='standalone-script-secret-key',
)

# Initialize Django
django.setup()

# Now you can use Django features
from django.contrib.auth.models import User
print(f"Total users: {User.objects.count()}")

if __name__ == "__main__":
run_standalone_task()

Key Requirements for Manual Configuration:

  • Uppercase Names: All setting names passed to configure() must be uppercase. The LazySettings.configure method raises a TypeError if a lowercase name is used.
  • Single Configuration: You can only call configure() once. Subsequent calls will raise a RuntimeError.
  • Global Defaults: Any settings not provided in configure() will be pulled from django.conf.global_settings.

Checking if a Setting is Overridden

You can determine if a setting has been explicitly defined (overriding the global default) using the is_overridden() method. This is useful for logic that depends on whether a user has customized a specific behavior.

from django.conf import settings

if settings.is_overridden('TIME_ZONE'):
print(f"Custom timezone in use: {settings.TIME_ZONE}")
else:
print("Using default UTC timezone")

This method works for both module-based settings (via Settings.is_overridden) and manually configured settings (via UserSettingsHolder.is_overridden).

Creating Portable Settings References

When writing code that needs to be serialized—such as in database migrations or model deconstruction—use SettingsReference. This ensures that the reference to the setting (e.g., AUTH_USER_MODEL) is preserved rather than hardcoding the current value of that setting.

This pattern is used in django/db/models/fields/related.py to handle swappable models:

from django.conf import settings, SettingsReference

# Example of deconstructing a field that points to a swappable model
def deconstruct_swappable_field(field):
name, path, args, kwargs = field.deconstruct()

# If the field points to a swappable model like AUTH_USER_MODEL
if field.swappable_setting:
kwargs["to"] = SettingsReference(
kwargs["to"],
field.swappable_setting
)
return name, path, args, kwargs

Troubleshooting and Gotchas

ImproperlyConfigured: Requested setting, but settings are not configured

This occurs if you try to access settings before they have been initialized.

  • Solution: Ensure the DJANGO_SETTINGS_MODULE environment variable is set, or call settings.configure() before any attribute access.

ImproperlyConfigured: The SECRET_KEY setting must not be empty

The LazySettings.__getattr__ method explicitly checks for SECRET_KEY. If it is missing or empty, Django will refuse to start.

  • Solution: Ensure SECRET_KEY is defined in your settings module or passed to settings.configure().

RuntimeError: Settings already configured

This happens if settings.configure() is called after settings have already been accessed or configured.

  • Solution: Check the settings.configured property before calling configure() if your script might be imported multiple times.
if not settings.configured:
settings.configure(...)

Automatic URL Prefixing

Note that LazySettings automatically modifies MEDIA_URL and STATIC_URL if they are relative paths by prepending the script prefix. This is handled internally in __getattr__ via the _add_script_prefix static method.