Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recommended approach for loading CouchDB design documents in Python?

Tags:

python

couchdb

I'm very new to couch, but I'm trying to use it on a new Python project, and I'd like to use python to write the design documents (views), also. I've already configured Couch to use the couchpy view server, and I can confirm this works by entering some simple map/reduce functions into Futon.

Are there any official recommendations on how to load/synchronize design documents when using Python's couchdb module?

I understand that I can post design documents to "install" them into Couch, but my question is really around best practices. I need some kind of strategy for deploying, both in development environments and in production environments. My intuition is to create a directory and store all of my design documents there, then write some kind of sync script that will upload each one into couch (probably just blindly overwriting what's already there). Is this a good idea?

The documentation for "Writing views in Python" is 5 sentences, and really just explains how to install couchpy. On the project's google code site, there is mention of a couchdb.design module that sounds like it might help, but there's no documentation (that I can find). The source code for that module indicates that it does most of what I'm interested in, but it stops short of actually loading files. I think I should do some kind of module discovery, but I've heard that's non-Pythonic. Advice?

Edit:

In particular, the idea of storing my map/reduce functions inside string literals seems completely hacky. I'd like to write real python code, in a real module, in a real package, with real unit tests. Periodically, I'd like to synchronize my "couch views" package with a couchdb instance.

like image 775
Mark E. Haase Avatar asked May 22 '12 18:05

Mark E. Haase


1 Answers

Here's an approach that seems reasonable. First, I subclass couchdb.design.ViewDefinition. (Comments and pydocs removed for brevity.)

import couchdb.design
import inflection

DESIGN_NAME="version"

class CurrentVersion(couchdb.design.ViewDefinition):
    def __init__(self):

        map_fun = self.__class__.map

        if hasattr(self.__class__, "reduce"):
            reduce_fun = self.__class__.reduce
        else:
            reduce_fun = None

        super_args = (DESIGN_NAME,
                      inflection.underscore(self.__class__.__name__),
                      map_fun,
                      reduce_fun,
                      'python')

        super(CurrentVersion, self).__init__(*super_args)

    @staticmethod
    def map(doc):
        if 'version_key' in doc and 'created_ts' in doc:
            yield (doc['version_key'], [doc['_id'], doc['created_ts']])

    @staticmethod
    def reduce(keys, values, rereduce):
        max_index = 0

        for index, value in enumerate(values):
            if value[1] > values[max_index][1]:
                max_index = index

        return values[max_index]

Now, if I want to synchronize:

import couchdb.design
from couchview.version import CurrentVersion

db = get_couch_db() # omitted for brevity
couchdb.design.ViewDefinition.sync_many(db, [CurrentVersion()], remove_missing=True)

The benefits of this approach are:

  1. Organization. All designs/views exist as modules/classes (respectively) located in a single package.
  2. Real code. My text editor will highlight syntax. I can write unit tests against my map/reduce functions.

The ViewDefinition subclass can also be used for querying.

current_version_view = couchview.version.CurrentVersion()
result = current_version_view(self.db, key=version_key)

It's still not ready for production, but I think this is a big step closer compared to storing map/reduce functions inside string literals.

Edit: I eventually wrote a couple blog posts on this topic, since I couldn't find any other sources of advice:

  • http://markhaase.com/2012/06/23/couchdb-views-in-python/
  • http://markhaase.com/2012/07/01/unit-tests-for-python-couchdb-views/
like image 194
Mark E. Haase Avatar answered Nov 08 '22 13:11

Mark E. Haase