summaryrefslogtreecommitdiff
path: root/storages/backends/couchdb.py
diff options
context:
space:
mode:
Diffstat (limited to 'storages/backends/couchdb.py')
-rw-r--r--storages/backends/couchdb.py135
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()
+
+