CouchDB documents -> Python objects

It’s getting late so I’ll keep this short.

Lately, I’ve been working a bit with CouchDB and trying to fully grasp the whole schema-free thing. I’m actually fond of Python objects, so I really wanted a way to work with arbitrary database fields as if they were object properties.

Here’s my intermediate class, CouchObject.

The example here is particularly interesting because Item is actually two kinds of items: files and folders. They share many fields such as name, directory, and user_id. However, files have some additional fields: content_type and size. By using CouchObject, I can be cool and only store the fields necessary for the particular kind of Item in the CouchDB document.

I could also later add another type of object (really, I only have one right now) and all I’d need to do is define different names for all_fields.

Obviously it’s not perfect, but it’s 2 AM here and I wanted to share a little about what I’ve been doing in my free time. Feedback welcome.

P.S.  I’m a little scared about using exec and especially without knowing what type of stuff is being passed in the dictionary. Eeek.

Update: Obviously I should not write code very late at night. I’m (*gasp*) actually a morning person. Thanks to everyone who pointed out that yes, exec is horrible when get/setattr would have done just fine. I’ve updated the code link accordingly. Thanks.

I’m going to publish your comments anyways so I hope that’s okay. The quantity of responses is quite scary in itself since I don’t track how many people read my blog.

After the whole fuss about rounding numbers, I was considering changing the blog post to make myself look a bit better. I decided against this for several reasons:

1. My blog should reflect me and I do a lot of stupid things. For example, I regularly spill food on myself while eating. You would think that after 26 years of eating food, I’d be able to do that correctly. Obviously not.
2. It wasn’t a big change. Really, like 3 lines of code. I had the right idea, even at 2 AM. I also knew I was probably doing something wrong. So yeah, not that bad.
3. It’s good to be wrong sometimes. We all make mistakes. So now I’m declaring, yes, I wrote some stupid code and it’s okay. It’s okay for us all to write stupid code sometimes.*

* My blog was originally titled “Leah Culver’s Stupid Blog” before I decided “Leah Culver” was more professional. I think I might change it back.

Update (part 2): I forgot to mention that the couchdb lib I’m using is couchdb-python.

10 Comments

  1. Cam MacRae

    Posted November 25, 2008 at 3:11 am | Permalink

    Late here also so I may have missed the point, but why the execs and not setattr / getattr?

    In [13]: class A(object):
    ….: pass
    ….:

    In [14]: a = A()

    In [15]: setattr(a, ‘myvar’, 12)

    In [16]: a.myvar
    Out[16]: 12

    In the class you’d do setattr(self, ‘myvar’, 12)

    c.

  2. Posted November 25, 2008 at 3:14 am | Permalink

    Isn’t getattr a better solution than exec ?

    http://effbot.org/zone/python-getattr.htm

  3. Posted November 25, 2008 at 4:08 am | Permalink

    You should be scared :)

    getattr and setattr are your friend. Code generation is dangerous and slow for this sort of purpose.

  4. Posted November 25, 2008 at 5:49 am | Permalink

    You do realize that Python allows you to avoid exec here with, getattr, hasattr, setattr functions right? See also: http://www.python.org/doc/2.2/ref/attribute-access.html

  5. Joey

    Posted November 25, 2008 at 6:00 am | Permalink

    Is there any reason you can’t use the objects __dict__ to set the attributes?

    Look at lines 34 and 36 in my slightly modified version of your code: http://dpaste.com/hold/93619/

    I think this should work and avoids the unpleasantness of exec.

  6. Posted November 25, 2008 at 6:03 am | Permalink

    maybe i’m missing something, but can’t you change:
    exec ‘value = self.%s’ % field_name
    to:
    value = self.__dict__[field_name]
    ?

    and similar constructs for your other 2 uses of exec.

    In fact, to_dict could be:
    return [self.__dict__[fn] for fn in self.all_fields() if fn in self.__dict__ and self.__dict__[fn] is not None]

  7. Posted November 25, 2008 at 6:13 am | Permalink

    Hey Leah,

    It’s great to see you’re still playing with CouchDB!

    Just some comments on the code you posted.

    Instead of this:

    if field_name in kwargs and kwargs[field_name] is not None:

    You could do this:

    if kwargs.get(field_name, None) is not None:

    Instead of doing this:

    type(kwargs[field_name]) is types.StringType

    Try:

    isinstance(kwargs[field_name], basestring)

    This is really unsafe:

    exec “self.%s = None” % field_name

    Do this instead:

    setattr(self, field_name, None)

    This is also unsafe:

    exec ‘value = self.%s’ % field_name

    Try this:

    value = getattr(self, field_name)

    There are a few instance where you’ve used exec like above. You should always use getattr or setattr instead.

    Hope this helps!

    Noah

  8. Posted November 25, 2008 at 6:18 am | Permalink

    This should work in lieu of the exec stuff, I think:

    for field_name in self.all_fields():
    setattr(self, field_name, kwargs.get(field_name))

    Haven’t looked at it all that closely. But you should never really be “scared” of exec, because even if you do have to do something odd with it (you certainly don’t need to use it here), you can match inputs against regular expressions and use .isalpha() and so on.

    P.S. I… no, nevermind.

  9. Posted November 25, 2008 at 9:14 am | Permalink

    Thanks for the writeup and sample code. Looks like a suitable alternative to BigTable if trying to prevent lock-in to App Engine.

    How about “Leah Culver’s candid blog”?

  10. Posted November 25, 2008 at 2:53 pm | Permalink

    I hope you keep up your candid approach to blogging - it’s refreshing