Source code for pouchdb.objectstorage
# Copyright 2014, Marten de Vries
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The objectstorage module allows you to save ordinary Python objects
directly into PouchDB. The (de)serializing is handled by the
`jsonpickle module <https://jsonpickle.github.io/>`_.
*******
Example
*******
>>> from pouchdb.objectstorage import load, store
>>> from jsonpickle._samples import Thing
>>>
>>> env = setup()
>>> db = env.PouchDB("objectstorage_test")
>>> thingy = Thing("abc")
>>>
>>> store(thingy, db, "the_id")
Thing("abc")
>>> new_thingy = load(db, "the_id")
>>> print new_thingy.name
abc
>>>
>>> env.destroy("objectstorage_test")
"""
try:
import jsonpickle.pickler
import jsonpickle.unpickler
except ImportError: #pragma: no cover
raise ImportError("pouchdb.objectstorage requires the jsonpickle module to be installed.")
import collections
import pouchdb.info
_KEY_PREFIX = pouchdb.info.name.lower() + "-"
[docs]def store(obj, db, id=None, rev=None, prefix=None):
"""Stores `obj` into `db`.
:param obj: Any Python object that ``jsonpickle`` can handle. (i.e.
most objects.)
:param pouchdb.SyncPouchDB db: A database as returned by
``pouchdb.setup().PouchDB(db_name)``. This **can't** be a
database as generated by the asynchronous API.
:param str id: Used as the ``_id`` field in PouchDB. If this is
``None`` and ``obj.id`` exists, that is used. The fallback is a
UUID.
:param str rev: Used as the ``_rev`` field in PouchDB. If this is
``None`` and ``obj.rev`` exists, that is used. Otherwise a new
document is assumed (with no ``_rev`` yet).
:param str prefix: a serialization as returned by ``jsonpickle`` can
have a field starting with '_' in a dictionary. That clashes
with PouchDB, which can't. To work around that, every key is
prefixed with this argument **if the situation occurs**. When
no prefix is specified it'll be: '``%s``'.
:returns: `obj` -- the same object as the parameter on which
``obj.id`` and ``obj.rev`` will have been set.
"""
if not prefix:
prefix = _KEY_PREFIX
if hasattr(obj, "id"):
id = id or obj.id
del obj.id
if hasattr(obj, "rev"):
rev = rev or obj.rev
del obj.rev
doc = jsonpickle.pickler.Pickler().flatten(obj)
if any(k.startswith("_") for k in doc):
doc = {prefix + k:v for k, v in doc.iteritems()}
if id:
doc["_id"] = id
if rev:
doc["_rev"] = rev
resp = db.post(doc)
obj.id = resp.id
obj.rev = resp.rev
return obj
store.__doc__ = store.__doc__ % _KEY_PREFIX
[docs]def load(db, id, rev=None, prefix=None):
"""Loads an earlier under `id` stored object from `db`.
:param str id: The ``_id`` to look for in the database.
:param str rev: The ``_rev`` to look for in the database. When not
specified the latest revision is used.
:param str prefix: see :func:`store`'s parameter with the same name.
Should equal the one used to store the object, otherwise the
result is **undefined**.
:returns: the earlier stored object.
"""
if not prefix:
prefix = _KEY_PREFIX
doc = db.get(id, rev=rev)
data = {}
for key, value in doc.iteritems():
if key in ["_id", "_rev"]:
continue
if key.startswith(prefix):
key = key[len(prefix):]
data[key] = _normalizeDicts(value)
obj = jsonpickle.unpickler.Unpickler().restore(data)
obj.id = doc["_id"]
obj.rev = doc["_rev"]
return obj
def _normalizeDicts(obj):
if isinstance(obj, collections.Mapping):
return {k:_normalizeDicts(v) for k, v in obj.iteritems()}
if isinstance(obj, collections.MutableSequence):
return [_normalizeDicts(x) for x in obj]
return obj