Document StorageHandler as public API.

Django 4.2 introduced the new storages API in order to allow folks to define and use multiple storages.

Internally storages is an instance of the StorageHandler class.

I would like to minimally document that, in order to officially make it available to third-party package maintainers.

Packages need to provide storage instances, and in order to opt-in to the new storages API need to be able to allow users to provide a storage for a known alias (the name of which is most easily given by a setting). If the user provides such a storage that should be used, otherwise the third-party package should provide a default.

That would look something like this:

alias = # From e.g. settings
storage_class = # Default class to use if not overridden. 
try:
    # Did the user provide the alias? 
    return storages[alias]
except InvalidStorageError:
    # Update storages to provide a default. 
    storages.backends[alias] = {
        "BACKEND": storage_class
    }
    return storages[alias]

This works perfectly, but, in order to be safe, the backends dict should be documented.

The create_storage() method is also useful, and should be made available.

FWIW this comes up Updating django-compressor to use the new API but the same would be necessary for any third-party package wanting to allow users to use the storages API in order to provide optional configuration overrides.

1 Like

I’ve created a PR so you can see what I have in mind.

+1 to documenting storages. I envisage it to be similar to the documentation for caches.

Also, I found that django.db.connections does not have proper reference documentation, just some usage coverage in Using raw cursors with multiple databases and Executing custom SQL directly. I think it deserves a similar section to caches.

I am not so hot on encouraging the pattern of inserting backends into the handler class. Why can’t the package require users to define the key in STORAGES? That seems more transparent to me, and more aligned with the other “handlers” (caches / databases).

That’s a good question. The answer is that 99% of users are never going to customise the storage here, they’re just going to use the default. So it’s adding a quite a big extra boilerplatey-horrible-pointlessness to make them add the STORAGES entry.

The pattern we want is “Did they override?” If so use, otherwise provide the default. We could not add the storage to storages but then next call we can’t easily re-use the instance.

Adding it to the backends dict, and then accessing it works just nicely and resolves all these issues.

I’m still open to another way of letting folks opt-in to the storages API with third-party packages providing the default, if there are any (but this have been bubbling on the compressor repo since 4.2, and this is the least touch solution that’s presented so far).

All I really want with the PR here is the stability guarantee that we can use the StorageHandler as implemented without a non-deprecation path breaking change. (i.e. We could just mention backends and not have the example, for example.)

Update: FWIW I was thinking about an alternate for compressor not updating backends, for which we’d need to instantiate a storage ourselves. That would work, but we’d loose the caching. For that though we’d need the create_storage() method, or we’d have to vendor that, with the complications on keeping it up to date that that would entail, so it would still be better to have it part of Django’s public API.