from django.core.files.storage import Storage from django.conf import settings ''' TODO: Create a signal to remove instances from cache if modified ''' 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 _proxy(self, method, name, *args, **kwargs): print('PROXY', method, name, *args, **kwargs) storage, p = self.split(name) result = getattr(self.get_storage(storage), method)(p, *args, **kwargs) print("RESULT", result) return result def _proxy_name(self, method, name, *args, **kwargs): print('PROXY_NAME', method, name, *args, **kwargs) storage, p = self.split(name) result = getattr(self.get_storage(storage), method)(p, *args, **kwargs) print("RESULT", result) return f"{storage}/{result}" def _open(self, name, mode='rb'): return self._proxy('_open', name, mode) def _save(self, name, content, max_length=None): return self._proxy_name('_save', name, content, max_length) def exists(self, name): return self._proxy('exists', name) def delete(self, name): return self._proxy('delete', name) def url(self, name): return self._proxy('url', name) def get_accessed_time(self, name): return self._proxy('get_accessed_time', name) def get_alternative_name(self, file_root, file_ext): return self._proxy_name('get_alternative_name', file_root, file_ext) def get_available_name(self, name, max_length=None): return self._proxy_name('get_available_name', name, max_length) def get_modified_time(self, name): return self._proxy('get_modified_time', name) def get_valid_name(self, name): return self._proxy_name('get_valid_name', name) 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]