Mapping from raw JSON data structures to Python objects and vice versa.
While this module is partly based on couchdb.mapping, it doesn’t try to provide the exact same API. The largest differences can be found in the DictField class, the ListField class, the Document.query() method and the fact that there’s no Mapping class.
Examples on how to use this module:
>>> env = setup()
>>> db = env.PouchDB('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 pouchdb.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(added=datetime.datetime(...), age=42, name='John Doe')
>>> 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
>>> print person.name
John Doe
>>> person.age
42
>>> person.added
datetime.datetime(...)
To update a document, simply set the attributes, and then call the Document.store() method:
>>> person.name = 'John R. Doe'
>>> person.store(db)
Person(added=datetime.datetime(...), age=42, name='John R. Doe')
If you retrieve the document from the server again, you should be getting the updated data:
>>> person = Person.load(db, person.id)
>>> print person.name
John R. Doe
>>> person.rev != old_rev
True
>>> env.destroy('python-tests')
The document class by default already has two defined fields: id and rev. They’re used to determine the values of CouchDB’s and PouchDB’s _id and _rev special attributes.
Returns the fields with their values in the form of a dict.
>>> class Post(Document):
... title = TextField()
... author = TextField()
>>> post = Post(id='foo-bar', title='Foo bar', author='Joe')
>>> printjson(post.as_dict)
{"_id": "foo-bar", "author": "Joe", "title": "Foo bar"}
Load a specific document from the given database.
Parameters: |
|
---|---|
Returns: | the Document instance |
Raises pouchdb.PouchDBError: | |
when the document with id can’t be found. |
Same as pouchdb.AbstractPouchDB.query(), but replaces each row with an instance of (your subclass of) Document. Additional attributes set on these objects are key and value. Sets options["include_docs"] to True (otherwise the mapping doesn’t make any sense). Except when there’s a reduce function, in that case no mapping takes place at all.
Descriptor that can be used to bind a view definition to a property of a Document class.
>>> from pouchdb.mapping import TextField, IntegerField, ViewField
>>> class Person(Document):
... name = TextField()
... age = IntegerField()
... by_name = ViewField('people', '''\
... function(doc) {
... emit(doc.name, doc);
... }''')
>>> Person.by_name
<ViewField 'people'>
>>> 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.
>>> db = setup().PouchDB('python-tests')
>>> Person(name='test').store(db)
Person(name='test')
>>> printjson(Person.by_name(db, limit=3))
{"offset": 0, "rows": ["Person(name='test')"], "total_rows": 1}
The results produced by the view are automatically wrapped in the Document subclass the descriptor is bound to. In this example, it returns instances of the Person class. This can be done because the ViewField automatically includes the include_docs option when making a query. See for more info the Document.query() method.
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
<ViewField 'people'>
>>> print Person.by_name.map_fun
def by_name(doc):
yield doc['name'], doc
Field type for nested dictionaries.
>>> from pouchdb.mapping import DictField
>>> db = setup()['python-tests']
>>> class Post(Document):
... title = TextField()
... content = TextField()
... author = DictField()
... extra = DictField()
>>> post = Post(
... title='Foo bar',
... author=dict(name='John Doe',
... email='john@doe.com'),
... extra=dict(foo='bar'),
... )
>>> post.store(db)
Post(author={'email': 'john@doe.com', 'name': 'John Doe'}, extra={'foo': 'bar'}, title='Foo bar')
>>> post = Post.load(db, post.id)
>>> print post.author.name
John Doe
>>> print post.author.email
john@doe.com
>>> printjson(post.extra)
{"foo": "bar"}
>>> printjson(db.destroy())
{"ok": true}
Field type for sequences of other fields.
>>> from pouchdb.mapping import ListField
>>> import dateutil.parser
>>> import copy
>>> db = setup()["python-tests"]
>>>
>>> def load(rows):
... for row in rows:
... row["time"] = dateutil.parser.parse(row["time"])
... return rows
...
>>> def serialize(input):
... rows = copy.deepcopy(input)
... for row in rows:
... row["time"] = row["time"].isoformat()
... return rows
...
>>> class Post(Document):
... title = TextField()
... content = TextField()
... pubdate = DateTimeField(default=datetime.now)
... comments = ListField(jsonSerializable=serialize, toPy=load)
...
>>> post = Post(title='Foo bar')
>>> post.comments.append(dict(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]
>>> print comment['author']
myself
>>> print comment['content']
Bla bla
>>> comment['time']
datetime.datetime(...)
>>> printjson(db.destroy())
{"ok": true}
Mapping field for boolean values.
Mapping field for date/time values.
Mapping field for decimal values.