4. Mapping CouchDB documents to Python objects: couchdb.mapping — couchdb-python 1.0 documentation

# 4. Mapping CouchDB documents to Python objects: couchdb.mapping

Mapping from raw JSON data structures to Python objects and vice versa.

    >>> from couchdb import Server
    >>> server = Server()
    >>> db = server.create('python-tests')

To define a document mapping, you declare a Python class inherited from Document, and add any number of Field attributes:

    >>> from datetime import datetime
    >>> from couchdb.mapping import Document, TextField, IntegerField, DateTimeField
    >>> class Person(Document):
    ...     name = TextField()
    ...     age = IntegerField()
    ...     added = DateTimeField(default=datetime.now)
    >>> person = Person(name='John Doe', age=42)
    >>> person.store(db)
    <Person ...>
    >>> person.age
    42

You can then load the data from the CouchDB server through your Document subclass, and conveniently access all attributes:

    >>> person = Person.load(db, person.id)
    >>> old_rev = person.rev
    >>> person.name
    u'John Doe'
    >>> person.age
    42
    >>> person.added
    datetime.datetime(...)

To update a document, simply set the attributes, and then call the `store()` method:

    >>> person.name = 'John R. Doe'
    >>> person.store(db)
    <Person ...>

If you retrieve the document from the server again, you should be getting the updated data:

    >>> person = Person.load(db, person.id)
    >>> person.name
    u'John R. Doe'
    >>> person.rev != old_rev
    True

    >>> del server['python-tests']

## 4.1. Field types

class `couchdb.mapping.` `TextField`(name=None, default=None)

  Mapping field for string values.

class `couchdb.mapping.` `FloatField`(name=None, default=None)

  Mapping field for float values.

class `couchdb.mapping.` `IntegerField`(name=None, default=None)

  Mapping field for integer values.

class `couchdb.mapping.` `LongField`(name=None, default=None)

  Mapping field for long integer values.

class `couchdb.mapping.` `BooleanField`(name=None, default=None)

  Mapping field for boolean values.

class `couchdb.mapping.` `DecimalField`(name=None, default=None)

  Mapping field for decimal values.

class `couchdb.mapping.` `DateField`(name=None, default=None)

  Mapping field for storing dates.

      >>> field = DateField()
      >>> field._to_python('2007-04-01')
      datetime.date(2007, 4, 1)
      >>> field._to_json(date(2007, 4, 1))
      '2007-04-01'
      >>> field._to_json(datetime(2007, 4, 1, 15, 30))
      '2007-04-01'

class `couchdb.mapping.` `DateTimeField`(name=None, default=None)

  Mapping field for storing date/time values.

      >>> field = DateTimeField()
      >>> field._to_python('2007-04-01T15:30:00Z')
      datetime.datetime(2007, 4, 1, 15, 30)
      >>> field._to_json(datetime(2007, 4, 1, 15, 30, 0, 9876))
      '2007-04-01T15:30:00Z'
      >>> field._to_json(date(2007, 4, 1))
      '2007-04-01T00:00:00Z'

class `couchdb.mapping.` `DictField`(mapping=None, name=None, default=None)

  Field type for nested dictionaries.

      >>> from couchdb import Server
      >>> server = Server()
      >>> db = server.create('python-tests')

      >>> class Post(Document):
      ...     title = TextField()
      ...     content = TextField()
      ...     author = DictField(Mapping.build(
      ...         name = TextField(),
      ...         email = TextField()
      ...     ))
      ...     extra = DictField()

      >>> post = Post(
      ...     title='Foo bar',
      ...     author=dict(name='John Doe',
      ...                 email='john@doe.com'),
      ...     extra=dict(foo='bar'),
      ... )
      >>> post.store(db)
      <Post ...>
      >>> post = Post.load(db, post.id)
      >>> post.author.name
      u'John Doe'
      >>> post.author.email
      u'john@doe.com'
      >>> post.extra
      {u'foo': u'bar'}

      >>> del server['python-tests']

class `couchdb.mapping.` `ListField`(field, name=None, default=None)

  Field type for sequences of other fields.

      >>> from couchdb import Server
      >>> server = Server()
      >>> db = server.create('python-tests')

      >>> class Post(Document):
      ...     title = TextField()
      ...     content = TextField()
      ...     pubdate = DateTimeField(default=datetime.now)
      ...     comments = ListField(DictField(Mapping.build(
      ...         author = TextField(),
      ...         content = TextField(),
      ...         time = DateTimeField()
      ...     )))

      >>> post = Post(title='Foo bar')
      >>> post.comments.append(author='myself', content='Bla bla',
      ...                      time=datetime.now())
      >>> len(post.comments)
      1
      >>> post.store(db)
      <Post ...>
      >>> post = Post.load(db, post.id)
      >>> comment = post.comments[0]
      >>> comment['author']
      u'myself'
      >>> comment['content']
      u'Bla bla'
      >>> comment['time']
      u'...T...Z'

      >>> del server['python-tests']

class `couchdb.mapping.` `ViewField`(design, map_fun, reduce_fun=None, name=None, language='javascript', wrapper=<object object>, **defaults)

  Descriptor that can be used to bind a view definition to a property of a Document class.

      >>> class Person(Document):
      ...     name = TextField()
      ...     age = IntegerField()
      ...     by_name = ViewField('people', '''\
      ...         function(doc) {
      ...             emit(doc.name, doc);
      ...         }''')
      >>> Person.by_name
      <ViewDefinition '_design/people/_view/by_name'>

      >>> print(Person.by_name.map_fun)
      function(doc) {
          emit(doc.name, doc);
      }

  That property can be used as a function, which will execute the view.

      >>> from couchdb import Database
      >>> db = Database('python-tests')

      >>> Person.by_name(db, count=3)
      <ViewResults <PermanentView '_design/people/_view/by_name'> {'count': 3}>

  The results produced by the view are automatically wrapped in the Document subclass the descriptor is bound to. In this example, it would return instances of the Person class. But please note that this requires the values of the view results to be dictionaries that can be mapped to the mapping defined by the containing Document class. Alternatively, the `include_docs` query option can be used to inline the actual documents in the view results, which will then be used instead of the values.

  If you use Python view functions, this class can also be used as a decorator:

      >>> class Person(Document):
      ...     name = TextField()
      ...     age = IntegerField()
      ...
      ...     @ViewField.define('people')
      ...     def by_name(doc):
      ...         yield doc['name'], doc

      >>> Person.by_name
      <ViewDefinition '_design/people/_view/by_name'>

      >>> print(Person.by_name.map_fun)
      def by_name(doc):
          yield doc['name'], doc