[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