from django.core.files.storage import Storage class MultiStorage(Storage): ''' Django storage class that proxies multiple storage classes. Uses a name prefix to determine store e.g. 'images:foo/bar.jpg' ''' def __init__(self, config=None): self.config = config or getattr(settings, 'BRING_YOUR_OWN_STORAGE', {}) self._cache = {} def get_storage(self, name): if name not in self._cache: config = self.config[name] self._cache[name] = get_storage_class(config['storage'])(**config['settings']) return self._cache[name] def split(self, name): return name.split(":", 1) def _open(self, name, mode='rb'): storage, p = self.split(name) return self.get_storage(storage)._open(p, mode) def _proxy(self, method, name, *args, **kwargs): storage, p = self.split(name) sname = getattr(self.get_storage(storage), method)(name, *args, **kwargs) if sname is not None: return f"{storage}:{sname}" def _save(self, name, content, max_length=None): return self._proxy('save', name, content, max_length) class BYOStorage(MultiStorage): ''' Database driven Bring-Your-Own-Storage Multiple storages can ''' def get_storage(self, name): if name not in self._cache: # use storage from config by default if name in self.config: return super(BYOStorage, self).get_storage(name) from .models import UserStorage obj = UserStorage.objects.get(name=name) self._cache[name] = obj.instance() return self._cache[name]