[Checkins] SVN: mongopersist/trunk/ - Feature: Added ``find_objects()`` and ``find_one_object()``to the collection
Stephen Richter
cvs-admin at zope.org
Sat Feb 9 05:26:55 UTC 2013
Log message for revision 129236:
- Feature: Added ``find_objects()`` and ``find_one_object()``to the collection
wrapper, so that whenever you get a collection from the data manager, you
can load objects directly through the find API.
- Feature: Added the ability for MongoContained objects to fully reference and
load their parents. This allows one to query mongo directly and create the
object from the doc without going through the right container, which you
might not know easily
Changed:
U mongopersist/trunk/CHANGES.txt
U mongopersist/trunk/setup.py
U mongopersist/trunk/src/mongopersist/datamanager.py
U mongopersist/trunk/src/mongopersist/serialize.py
U mongopersist/trunk/src/mongopersist/zope/container.py
U mongopersist/trunk/src/mongopersist/zope/tests/test_container.py
-=-
Modified: mongopersist/trunk/CHANGES.txt
===================================================================
--- mongopersist/trunk/CHANGES.txt 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/CHANGES.txt 2013-02-09 05:26:54 UTC (rev 129236)
@@ -2,12 +2,19 @@
CHANGES
=======
-0.7.8 (unreleased)
+0.8.0 (unreleased)
------------------
-- Nothing changed yet.
+- Feature: Added ``find_objects()`` and ``find_one_object()``to the collection
+ wrapper, so that whenever you get a collection from the data manager, you
+ can load objects directly through the find API.
+- Feature: Added the ability for MongoContained objects to fully reference and
+ load their parents. This allows one to query mongo directly and create the
+ object from the doc without going through the right container, which you
+ might not know easily.
+
0.7.7 (2013-02-08)
------------------
Modified: mongopersist/trunk/setup.py
===================================================================
--- mongopersist/trunk/setup.py 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/setup.py 2013-02-09 05:26:54 UTC (rev 129236)
@@ -9,7 +9,7 @@
setup (
name='mongopersist',
- version='0.7.8.dev0',
+ version='0.8.0.dev0',
author = "Stephan Richter",
author_email = "stephan.richter at gmail.com",
description = "Mongo Persistence Backend",
Modified: mongopersist/trunk/src/mongopersist/datamanager.py
===================================================================
--- mongopersist/trunk/src/mongopersist/datamanager.py 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/src/mongopersist/datamanager.py 2013-02-09 05:26:54 UTC (rev 129236)
@@ -14,6 +14,7 @@
"""Mongo Persistent Data Manager"""
from __future__ import absolute_import
import UserDict
+import bson
import logging
import transaction
import sys
@@ -111,6 +112,23 @@
self.__dict__['collection'] = collection
self.__dict__['_datamanager'] = datamanager
+ def find_objects(self, *args, **kw):
+ docs = self.find(*args, **kw)
+ coll = self.collection.name
+ dbname = self.collection.database.name
+ for doc in docs:
+ dbref = bson.dbref.DBRef(coll, doc['_id'], dbname)
+ self._datamanager._latest_states[dbref] = doc
+ yield self._datamanager.load(dbref)
+
+ def find_one_object(self, *args, **kw):
+ doc = self.find_one(*args, **kw)
+ coll = self.collection.name
+ dbname = self.collection.database.name
+ dbref = bson.dbref.DBRef(coll, doc['_id'], dbname)
+ self._datamanager._latest_states[dbref] = doc
+ return self._datamanager.load(dbref)
+
def __getattr__(self, name):
attr = getattr(self.collection, name)
if MONGO_ACCESS_LOGGING and name in self.LOGGED_METHODS:
Modified: mongopersist/trunk/src/mongopersist/serialize.py
===================================================================
--- mongopersist/trunk/src/mongopersist/serialize.py 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/src/mongopersist/serialize.py 2013-02-09 05:26:54 UTC (rev 129236)
@@ -511,6 +511,9 @@
coll = self._jar.get_collection(
obj._p_oid.database, obj._p_oid.collection)
doc = coll.find_one({'_id': obj._p_oid.id})
+ # Check that we really have a state doc now.
+ if doc is None:
+ raise ImportError(obj._p_oid)
# Create a copy of the doc, so that we can modify it.
state_doc = doc.copy()
# Remove unwanted attributes.
Modified: mongopersist/trunk/src/mongopersist/zope/container.py
===================================================================
--- mongopersist/trunk/src/mongopersist/zope/container.py 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/src/mongopersist/zope/container.py 2013-02-09 05:26:54 UTC (rev 129236)
@@ -28,19 +28,43 @@
class MongoContained(contained.Contained):
+ _v_name = None
+ _m_name_attr = None
+ _m_name_getter = None
+ _m_name_setter = None
+
+ _m_parent_attr = None
+ _m_parent_getter = None
+ _m_parent_setter = None
+ _v_parent = None
+
@getproperty
def __name__(self):
- return getattr(self, '_v_key', None)
+ if self._v_name is None:
+ if self._m_name_attr is not None:
+ self._v_name = getattr(self, self._m_name_attr, None)
+ elif self._m_name_getter is not None:
+ self._v_name = self._m_name_getter()
+ return self._v_name
@setproperty
def __name__(self, value):
- setattr(self, '_v_key', value)
+ if self._m_name_setter is not None:
+ self._m_name_setter(value)
+ self._v_name = value
@getproperty
def __parent__(self):
- return getattr(self, '_v_parent', None)
+ if self._v_parent is None:
+ if self._m_parent_attr is not None:
+ self._v_parent = getattr(self, self._m_parent_attr, None)
+ elif self._m_parent_getter is not None:
+ self._v_parent = self._m_parent_getter()
+ return self._v_parent
@setproperty
def __parent__(self, value):
- setattr(self, '_v_parent', value)
+ if self._m_parent_setter is not None:
+ self._m_parent_setter(value)
+ self._v_parent = value
class SimpleMongoContainer(sample.SampleContainer, persistent.Persistent):
@@ -62,7 +86,7 @@
def __getitem__(self, key):
obj = super(SimpleMongoContainer, self).__getitem__(key)
- obj._v_key = key
+ obj._v_name = key
obj._v_parent = self
return obj
@@ -70,14 +94,14 @@
'''See interface `IReadContainer`'''
obj = super(SimpleMongoContainer, self).get(key, default)
if obj is not default:
- obj._v_key = key
+ obj._v_name = key
obj._v_parent = self
return obj
def items(self):
items = super(SimpleMongoContainer, self).items()
for key, obj in items:
- obj._v_key = key
+ obj._v_name = key
obj._v_parent = self
return items
@@ -161,8 +185,12 @@
filter[key] = value
def _locate(self, obj, doc):
- obj._v_key = doc[self._m_mapping_key]
- obj._v_parent = self
+ # Helper method that is only used when locating items that are already
+ # in the container and are simply loaded from Mongo.
+ if obj.__name__ is None:
+ obj.__name__ = doc[self._m_mapping_key]
+ if obj.__parent__ is None:
+ obj._v_parent = self
def _load_one(self, doc):
# Create a DBRef object and then load the full state of the object.
@@ -190,7 +218,7 @@
return obj
def _real_setitem(self, key, value):
- # This call by iteself caues the state to change _p_changed to True.
+ # This call by iteself causes the state to change _p_changed to True.
if self._m_mapping_key is not None:
setattr(value, self._m_mapping_key, key)
if self._m_parent_key is not None:
@@ -295,7 +323,7 @@
return True
def _locate(self, obj, doc):
- obj._v_key = unicode(doc['_id'])
+ obj._v_name = unicode(doc['_id'])
obj._v_parent = self
def __getitem__(self, key):
Modified: mongopersist/trunk/src/mongopersist/zope/tests/test_container.py
===================================================================
--- mongopersist/trunk/src/mongopersist/zope/tests/test_container.py 2013-02-08 17:09:28 UTC (rev 129235)
+++ mongopersist/trunk/src/mongopersist/zope/tests/test_container.py 2013-02-09 05:26:54 UTC (rev 129236)
@@ -54,6 +54,199 @@
pass
+def doctest_MongoContained_simple():
+ """MongoContained: simple use
+
+ The simplest way to use MongoContained is to use it without any special
+ modification. In this case it is required that the container always sets
+ the name and parent after loading the item. It can do so directly by
+ setting ``_v_name`` and ``_v_parent`` so that the persistence mechanism
+ does not kick in.
+
+ >>> class Simples(container.MongoContainer):
+ ... def __init__(self, name):
+ ... super(Simples, self).__init__()
+ ... self.name = name
+ ... def __repr__(self):
+ ... return '<Simples %s>' %self.name
+
+ >>> class Simple(container.MongoContained, persistent.Persistent):
+ ... pass
+
+ Let's create a simple component and activate the persistence machinery:
+
+ >>> s = Simple()
+ >>> s._p_jar = dm
+
+ As you can see, the changed flag is not changed:
+
+ >>> s._p_changed
+ False
+ >>> s._v_name = 'simple'
+ >>> s._v_parent = Simples('one')
+ >>> s._p_changed
+ False
+
+ And accessing the name and parent works:
+
+ >>> s.__name__
+ 'simple'
+ >>> s.__parent__
+ <Simples one>
+
+ But assignment works as well.
+
+ >>> s.__name__ = 'simple2'
+ >>> s.__name__
+ 'simple2'
+ >>> s.__parent__ = Simples('two')
+ >>> s.__parent__
+ <Simples two>
+ >>> s._p_changed
+ True
+ """
+
+def doctest_MongoContained_proxy_attr():
+ """MongoContained: proxy attributes
+
+ It is also possible to use proxy attributes to reference the name and
+ parent. This allows you to have nice attribute names for storage in Mongo.
+
+ The main benefit, though is the ability of the object to load its
+ location, so that you can load the object without going through the
+ container and get full location path.
+
+ >>> class Proxies(container.MongoContainer):
+ ... def __init__(self, name):
+ ... super(Proxies, self).__init__()
+ ... self.name = name
+ ... def __repr__(self):
+ ... return '<Proxies %s>' %self.name
+
+ >>> class Proxy(container.MongoContained, persistent.Persistent):
+ ... _m_name_attr = 'name'
+ ... _m_parent_attr = 'parent'
+ ... def __init__(self, name, parent):
+ ... self.name = name
+ ... self.parent = parent
+
+ Let's create a proxy component and activate the persistence machinery:
+
+ >>> p = Proxy('proxy', Proxies('one'))
+ >>> p._p_jar = dm
+
+ So accessing the name and parent works:
+
+ >>> p.__name__
+ 'proxy'
+ >>> p.__parent__
+ <Proxies one>
+
+ But assignment is only stored into the volatile variables and the proxy
+ attribute values are not touched.
+
+ >>> p.__name__ = 'proxy2'
+ >>> p.__name__
+ 'proxy2'
+ >>> p.name
+ 'proxy'
+ >>> p.__parent__ = Proxies('two')
+ >>> p.__parent__
+ <Proxies two>
+ >>> p.parent
+ <Proxies one>
+
+ This behavior is intentional, so that containment machinery cannot mess
+ with the real attributes. Note that in practice, only MongoContainer sets
+ the ``__name__`` and ``__parent__`` and it should be always consistent
+ with the referenced attributes.
+
+ """
+
+def doctest_MongoContained_setter_getter():
+ """MongoContained: setter/getter functions
+
+ If you need ultimate flexibility of where to get and store the name and
+ parent, then you can define setters and getters.
+
+ >>> class Funcs(container.MongoContainer):
+ ... def __init__(self, name):
+ ... super(Funcs, self).__init__()
+ ... self.name = name
+ ... def __repr__(self):
+ ... return '<Funcs %s>' %self.name
+
+ >>> class Func(container.MongoContained, persistent.Persistent):
+ ... _m_name_getter = lambda s: s.name
+ ... _m_name_setter = lambda s, v: setattr(s, 'name', v)
+ ... _m_parent_getter = lambda s: s.parent
+ ... _m_parent_setter = lambda s, v: setattr(s, 'parent', v)
+ ... def __init__(self, name, parent):
+ ... self.name = name
+ ... self.parent = parent
+
+ Let's create a func component and activate the persistence machinery:
+
+ >>> f = Func('func', Funcs('one'))
+ >>> f._p_jar = dm
+
+ So accessing the name and parent works:
+
+ >>> f.__name__
+ 'func'
+ >>> f.__parent__
+ <Funcs one>
+
+ In this case, the setters are used, if the name and parent are changed:
+
+ >>> f.__name__ = 'func2'
+ >>> f.__name__
+ 'func2'
+ >>> f.name
+ 'func2'
+ >>> f.__parent__ = Funcs('two')
+ >>> f.__parent__
+ <Funcs two>
+ >>> f.parent
+ <Funcs two>
+ """
+
+
+def doctest_MongoContained_mixed():
+ """MongoContained: mixed usage
+
+ When the container is stored in the ZODB or another persistence mechanism,
+ a mixed usage of proxy attributes and getter/setter functions is the best
+ appraoch.
+
+ >>> class Mixers(btree.BTreeContainer):
+ ... def __init__(self, name):
+ ... super(Mixers, self).__init__()
+ ... self.name = name
+ ... def __repr__(self):
+ ... return '<Mixers %s>' %self.name
+ >>> mixers = Mixers('one')
+
+ >>> class Mixer(container.MongoContained, persistent.Persistent):
+ ... _m_name_attr = 'name'
+ ... _m_parent_getter = lambda s: mixers
+ ... def __init__(self, name):
+ ... self.name = name
+
+ Let's create a mixer component and activate the persistence machinery:
+
+ >>> m = Mixer('mixer')
+ >>> m._p_jar = dm
+
+ So accessing the name and parent works:
+
+ >>> m.__name__
+ 'mixer'
+ >>> m.__parent__
+ <Mixers one>
+ """
+
+
def doctest_SimpleMongoContainer_basic():
"""SimpleMongoContainer: basic
More information about the checkins
mailing list