As I briefly mentioned before, I've been moving this website over to CouchDB, after Mikeal sold me on how awesome it is. Unfortunately, as it's still such a new database, and as it approaches the database problem from such a different angle, it's not supported by Django, my framework of choice for web development.

Several people have moved their Django sites across though, and I wasn't about to be left behind. What immediately struck me was how well the CouchDB architecture fits with Django - a CouchDB Document is simply a collection of key value pairings, which is pretty much how Python stores attributes in a object or dictionary. It's trivial therefore to simply marshall your python objects into CouchDB documents. Although CouchDB is a schemaless database, it is still useful to have some notion of classes or types. The python CouchDB wrapper is still pretty immature and lacks a lot of documentation, so the only real way to get a proper feel for how it works is to go through the code, and it was as I was exploring it I found schema.py, a class specifically designed for this problem.

What was even more useful was the similarities with the Django ORM. I managed to port my old models across with only a tiny amount of modification to the code - instead of extending django.db.models.Model, you extend couchdb.schema.Document, and instead of ManyToMany relationships, you can put your data in a List field. Most of the rest of my models could be kept exactly the same.

The schemaless nature of the database was still available - I wanted to have a separate type for photos on my homepage then for essays, so I created a generic Article type and then extended it for my photos. This allowed me to override the content field with the following:

content = models.TextField(default = "")

def content(self):
    return get_template("photo_to_blog.html").render(Context({'photo':self}))

Rather than have to store the html for a photo, I can generate it on the fly with a template - this means when I update the structure of the photo html, I don't have to go back and change the database. CouchDB allows me to only store the content field when I need, and my page templates won't know the difference.


I've already hit a few issues. One that confused me a lot was when I tried to use the Django Paginator with my new CouchDB models. The schema code allows you to attach CouchDB views to a model. When I tried to get the results from the view to paginate I got a paginator with the correct length, but an empty objects list. The problem lies in the way the python couchdb library wraps result sets. It implements slicing as between the couchdb indices, rather than the list indices as Django and I expected. I threw a quick hacky patch together to make this work, but it could do with some love:

--- a/couchdb/client.py
+++ b/couchdb/client.py
@@ -713,8 +713,6 @@ class ViewResults(object):
     def getitem(self, key):
         options = self.options.copy()
         if type(key) is slice:
-            if isinstance(key.start, int) and isinstance(key.stop, int):
-                return list(self)[key.start:key.stop] 
             if key.start is not None:
                 options['startkey'] = key.start
             if key.stop is not None:

Another issue was that schema.py didn't actually seem to be wrapping the values that it got back from the database. Maybe I was using it wrong or maybe it's just because the library is young, but another hacky patch:

--- a/couchdb/schema.py
+++ b/couchdb/schema.py
@@ -164,6 +164,11 @@ class Schema(object):

     def wrap(cls, data):
         instance = cls()
+        for k,v in data.items():
+            if v and k in instance._fields.keys():
+                data[k] = instance._fields[k]._to_python(v)  
         instance._data = data
         return instance
     wrap = classmethod(wrap)

I'm sure that I'm going to hit a whole lot more issues before I can merge my branch down to the live site, but I've been surprised how nicely the technologies mesh so far.