diff options
Diffstat (limited to 'storages/backends/couchdb.py')
| -rw-r--r-- | storages/backends/couchdb.py | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/storages/backends/couchdb.py b/storages/backends/couchdb.py new file mode 100644 index 0000000..1e116da --- /dev/null +++ b/storages/backends/couchdb.py @@ -0,0 +1,135 @@ +""" +This is a Custom Storage System for Django with CouchDB backend. +Created by Christian Klein. +(c) Copyright 2009 HUDORA GmbH. All Rights Reserved. +""" +import os +from cStringIO import StringIO +from urlparse import urljoin +from urllib import quote_plus + +from django.conf import settings +from django.core.files import File +from django.core.files.storage import Storage +from django.core.exceptions import ImproperlyConfigured + +try: + import couchdb +except ImportError: + raise ImproperlyConfigured("Could not load couchdb dependency.\ + \nSee http://code.google.com/p/couchdb-python/") + +DEFAULT_SERVER= getattr(settings, 'COUCHDB_DEFAULT_SERVER', 'http://couchdb.local:5984') +STORAGE_OPTIONS= getattr(settings, 'COUCHDB_STORAGE_OPTIONS', {}) + + +class CouchDBStorage(Storage): + """ + CouchDBStorage - a Django Storage class for CouchDB. + + The CouchDBStorage can be configured in settings.py, e.g.:: + + COUCHDB_STORAGE_OPTIONS = { + 'server': "http://example.org", + 'database': 'database_name' + } + + Alternatively, the configuration can be passed as a dictionary. + """ + def __init__(self, **kwargs): + kwargs.update(STORAGE_OPTIONS) + self.base_url = kwargs.get('server', DEFAULT_SERVER) + server = couchdb.client.Server(self.base_url) + self.db = server[kwargs.get('database')] + + def _put_file(self, name, content): + self.db[name] = {'size': len(content)} + self.db.put_attachment(self.db[name], content, filename='content') + return name + + def get_document(self, name): + return self.db.get(name) + + def _open(self, name, mode='rb'): + couchdb_file = CouchDBFile(name, self, mode=mode) + return couchdb_file + + def _save(self, name, content): + content.open() + if hasattr(content, 'chunks'): + content_str = ''.join(chunk for chunk in content.chunks()) + else: + content_str = content.read() + name = name.replace('/', '-') + return self._put_file(name, content_str) + + def exists(self, name): + return name in self.db + + def size(self, name): + doc = self.get_document(name) + if doc: + return doc['size'] + return 0 + + def url(self, name): + return urljoin(self.base_url, + os.path.join(quote_plus(self.db.name), + quote_plus(name), + 'content')) + + def delete(self, name): + try: + del self.db[name] + except couchdb.client.ResourceNotFound: + raise IOError("File not found: %s" % name) + + #def listdir(self, name): + # _all_docs? + # pass + + +class CouchDBFile(File): + """ + CouchDBFile - a Django File-like class for CouchDB documents. + """ + + def __init__(self, name, storage, mode): + self._name = name + self._storage = storage + self._mode = mode + self._is_dirty = False + + try: + self._doc = self._storage.get_document(name) + + tmp, ext = os.path.split(name) + if ext: + filename = "content." + ext + else: + filename = "content" + attachment = self._storage.db.get_attachment(self._doc, filename=filename) + self.file = StringIO(attachment) + except couchdb.client.ResourceNotFound: + if 'r' in self._mode: + raise ValueError("The file cannot be reopened.") + else: + self.file = StringIO() + self._is_dirty = True + + @property + def size(self): + return self._doc['size'] + + def write(self, content): + if 'w' not in self._mode: + raise AttributeError("File was opened for read-only access.") + self.file = StringIO(content) + self._is_dirty = True + + def close(self): + if self._is_dirty: + self._storage._put_file(self._name, self.file.getvalue()) + self.file.close() + + |
