The “backend” pattern in Django settings
In the context of Django reusable apps, I often need to provide a way for the user to configure a connection (or set of) to some database or third-party service, and optionally be able to substitute the driver/adapter class with a custom one.
In that case I’ve settled on what I call the “backend” pattern (not sure if there is a generally-accepted name for this), similar to what Django already does with database backends.
It allows you to define the connections as follows:
PROCESS_CONTROLLERS = {
'default': {
'BACKEND': 'project.backends.TCPIPConnector',
'OPTIONS': {
'host': 'process-controller.example.com'
}
},
'backup': {
'BACKEND': 'project.backends.SerialConnector',
'OPTIONS': {
'port': '/dev/ttyUSB0'
}
}
}
And then get an instance of the BACKEND, instantiated with all the OPTIONS passed as **kwargs by calling it from anywhere in the code.
main_process_controller = get_and_instantiate_backend_from_settings('PROCESS_CONTROLLERS')
Here’s the implementation:
def get_and_instantiate_backend_from_settings(backend_type: str, key: str = "default"):
"""
Returns a backend instance from a Django settings structure as follows:
SOME_BACKEND_TYPE = {
'backend_key': {
'BACKEND': 'import.path.to.backend',
'OPTIONS: {} # optional dict of kwargs passed to backend constructor
}
}
So, `get_and_instantiate_backend_from_settings('SOME_BACKEND_TYPE', 'backend_key')`
would import `import.path.to.backend`, call it with `OPTIONS` passed as kwargs,
and returns the resulting instance.
"""
try:
configs = getattr(settings, backend_type)
except AttributeError:
raise ImproperlyConfigured(f"settings.{backend_type} is undefined.")
if config := configs.get(key):
if backend_import_path := config.get("BACKEND"):
try:
backend_class = import_string(backend_import_path)
except ImportError as e:
raise ImproperlyConfigured(
f'settings.{backend_type}["{key}"]["BACKEND"] could not be imported.'
) from e
backend_kwargs = config.get("OPTIONS", {})
return backend_class(**backend_kwargs)
else:
raise ImproperlyConfigured(
f'settings.{backend_type}["{key}"]["BACKEND"] does not exist or is falsy.'
)
else:
raise ImproperlyConfigured(
f'settings.{backend_type}["{key}"] does not exist or is falsy.'
)