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. TheLazySettings.configuremethod raises aTypeErrorif a lowercase name is used. - Single Configuration: You can only call
configure()once. Subsequent calls will raise aRuntimeError. - Global Defaults: Any settings not provided in
configure()will be pulled fromdjango.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_MODULEenvironment variable is set, or callsettings.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_KEYis defined in your settings module or passed tosettings.configure().
RuntimeError: Settings already configured
This happens if settings.configure() is called after settings have already been accessed or configured.
- Solution: Check the
settings.configuredproperty before callingconfigure()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.