[Checkins] SVN: z3c.relationfield/trunk/ Support for RelationList fields.

Martijn Faassen faassen at infrae.com
Thu Dec 11 13:31:31 EST 2008


Log message for revision 93915:
  Support for RelationList fields.
  

Changed:
  U   z3c.relationfield/trunk/CHANGES.txt
  U   z3c.relationfield/trunk/src/z3c/relationfield/README.txt
  U   z3c.relationfield/trunk/src/z3c/relationfield/__init__.py
  U   z3c.relationfield/trunk/src/z3c/relationfield/event.py
  U   z3c.relationfield/trunk/src/z3c/relationfield/interfaces.py
  U   z3c.relationfield/trunk/src/z3c/relationfield/schema.py

-=-
Modified: z3c.relationfield/trunk/CHANGES.txt
===================================================================
--- z3c.relationfield/trunk/CHANGES.txt	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/CHANGES.txt	2008-12-11 18:31:31 UTC (rev 93915)
@@ -4,7 +4,9 @@
 0.2 (unreleased)
 ================
 
-* ...
+* Added support for ``RelationList`` fields. This allows one to
+  maintain a list of ``RelationValue`` objects that will be cataloged
+  like the regular ``Relation`` fields.
 
 0.1 (2008-12-05)
 ================

Modified: z3c.relationfield/trunk/src/z3c/relationfield/README.txt
===================================================================
--- z3c.relationfield/trunk/src/z3c/relationfield/README.txt	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/src/z3c/relationfield/README.txt	2008-12-11 18:31:31 UTC (rev 93915)
@@ -382,6 +382,55 @@
   >>> l[0].from_path
   u'b'
 
+RelationList
+============
+
+Let's now experiment with the ``RelationList`` field which can be used
+to maintain a list of relations::
+
+  >>> from z3c.relationfield import RelationList
+  >>> class IMultiItem(Interface):
+  ...   rel = RelationList(title=u"Relation")
+
+We also define a class ``MultiItem`` that implements both
+``IMultiItem`` and the special
+``z3c.relationfield.interfaces.IHasRelations`` interface::
+
+  >>> class MultiItem(Persistent):
+  ...   implements(IMultiItem, IHasRelations)
+  ...   def __init__(self):
+  ...     self.rel = None
+
+We set up a few object we can then create relations between::
+
+  >>> root['multi1'] = MultiItem()
+  >>> root['multi2'] = MultiItem()
+  >>> root['multi3'] = MultiItem()
+
+Let's create a relation from ``multi1`` to both ``multi2`` and
+``multi3``::
+
+  >>> multi1_id = intids.getId(root['multi1'])
+  >>> multi2_id = intids.getId(root['multi2'])
+  >>> multi3_id = intids.getId(root['multi3'])
+
+  >>> root['multi1'].rel = [RelationValue(multi2_id),
+  ...                       RelationValue(multi3_id)]
+
+We need to notify that we modified the object
+
+  >>> notify(ObjectModifiedEvent(root['multi1']))
+
+Now that this is set up, let's verify whether we can find the
+proper relations in in the catalog::
+
+  >>> len(list(catalog.findRelations({'to_id': multi2_id})))
+  1
+  >>> len(list(catalog.findRelations({'to_id': multi3_id})))
+  1
+  >>> len(list(catalog.findRelations({'from_id': multi1_id})))
+  2
+
 Temporary relations
 ===================
 
@@ -412,8 +461,34 @@
   >>> realize_relations(root['d'])
   >>> notify(ObjectModifiedEvent(root['d']))
 
-The relation will now show up in the catalog::
+We can see the real relation object now::
 
+  >>> root['d'].rel
+  <z3c.relationfield.relation.RelationValue object at ...>
+
+The relation will also now show up in the catalog::
+
   >>> after2 = sorted(catalog.findRelations({'to_id': a_id}))
   >>> len(after2) > len(before)
   True
+
+Temporary relation values also work with ``RelationList`` objects::
+  
+  >>> root['multi_temp'] = MultiItem()
+  >>> root['multi_temp'].rel = [TemporaryRelationValue('a')]
+
+Let's convert this to a real relation::
+
+  >>> realize_relations(root['multi_temp'])
+  >>> notify(ObjectModifiedEvent(root['multi_temp']))
+
+Again we can see the real relation object when we look at it::
+
+  >>> root['multi_temp'].rel
+  [<z3c.relationfield.relation.RelationValue object at ...>]
+ 
+And we will now see this new relation appear in the catalog::
+
+  >>> after3 = sorted(catalog.findRelations({'to_id': a_id}))
+  >>> len(after3) > len(after2) 
+  True

Modified: z3c.relationfield/trunk/src/z3c/relationfield/__init__.py
===================================================================
--- z3c.relationfield/trunk/src/z3c/relationfield/__init__.py	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/src/z3c/relationfield/__init__.py	2008-12-11 18:31:31 UTC (rev 93915)
@@ -1,4 +1,4 @@
 from z3c.relationfield.relation import RelationValue, TemporaryRelationValue
 from z3c.relationfield.index import RelationCatalog
-from z3c.relationfield.schema import Relation
+from z3c.relationfield.schema import Relation, RelationList
 from z3c.relationfield.event import realize_relations

Modified: z3c.relationfield/trunk/src/z3c/relationfield/event.py
===================================================================
--- z3c.relationfield/trunk/src/z3c/relationfield/event.py	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/src/z3c/relationfield/event.py	2008-12-11 18:31:31 UTC (rev 93915)
@@ -11,7 +11,7 @@
 from zc.relation.interfaces import ICatalog
 
 from z3c.relationfield.interfaces import (IHasRelations,
-                                          IRelation,
+                                          IRelation, IRelationList,
                                           IRelationValue,
                                           ITemporaryRelationValue)
 
@@ -51,11 +51,16 @@
     addRelations(obj, event)
 
 def realize_relations(obj):
-    """Given an object, convert any temporary relatiosn on it to real ones.
+    """Given an object, convert any temporary relations on it to real ones.
     """
-    for name, relation in _potential_relations(obj):
+    for name, index, relation in _potential_relations(obj):
         if ITemporaryRelationValue.providedBy(relation):
-            setattr(obj, name, relation.convert())
+            if index is None:
+                # relation
+                setattr(obj, name, relation.convert())
+            else:
+                # relation list
+                getattr(obj, name)[index] = relation.convert()
 
 def _setRelation(obj, name, value):
     """Set a relation on an object.
@@ -83,18 +88,26 @@
 
     Only real relations are returned, not temporary relations.
     """
-    for name, relation in _potential_relations(obj):
+    for name, index, relation in _potential_relations(obj):
         if IRelationValue.providedBy(relation):
             yield name, relation
 
 def _potential_relations(obj):
-    """Given an object return tuples of name, relation value.
+    """Given an object return tuples of name, index, relation value.
 
     Returns both IRelationValue attributes as well as ITemporaryRelationValue
     attributes.
+
+    If this is a IRelationList attribute, index will contain the index
+    in the list. If it's a IRelation attribute, index will be None.
     """
     for iface in providedBy(obj).flattened():
         for name, field in getFields(iface).items():
             if IRelation.providedBy(field):
                 relation = getattr(obj, name)
-                yield name, relation
+                yield name, None, relation
+            if IRelationList.providedBy(field):
+                l = getattr(obj, name)
+                if l is not None:
+                    for i, relation in enumerate(l):
+                        yield name, i, relation

Modified: z3c.relationfield/trunk/src/z3c/relationfield/interfaces.py
===================================================================
--- z3c.relationfield/trunk/src/z3c/relationfield/interfaces.py	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/src/z3c/relationfield/interfaces.py	2008-12-11 18:31:31 UTC (rev 93915)
@@ -1,5 +1,5 @@
 from zope.interface import Interface, Attribute
-from zope.schema.interfaces import IField
+from zope.schema.interfaces import IField, IList
 
 class IHasRelations(Interface):
     """Marker interface indicating that the object has relations.
@@ -11,6 +11,9 @@
 class IRelation(IField):
     pass
 
+class IRelationList(IList):
+    pass
+
 class IRelationValue(Interface):
     """A relation between the parent object and another one.
 

Modified: z3c.relationfield/trunk/src/z3c/relationfield/schema.py
===================================================================
--- z3c.relationfield/trunk/src/z3c/relationfield/schema.py	2008-12-11 18:22:35 UTC (rev 93914)
+++ z3c.relationfield/trunk/src/z3c/relationfield/schema.py	2008-12-11 18:31:31 UTC (rev 93915)
@@ -3,11 +3,11 @@
 from lxml import etree
 
 from zope.interface import implements
-from zope.schema import Field
+from zope.schema import Field, List
 
 import z3c.schema2xml
 
-from z3c.relationfield.interfaces import IRelation
+from z3c.relationfield.interfaces import IRelation, IRelationList
 from z3c.relationfield.relation import TemporaryRelationValue
 
 class Relation(Field):
@@ -29,3 +29,28 @@
             return None
         path = element.text
         return TemporaryRelationValue(path)
+
+class RelationList(List):
+    implements(IRelationList)
+
+    value_type = Relation()
+    
+class RelationListGenerator(grok.Adapter):
+    """Export a relation list to XML.
+    """
+    grok.context(IRelationList)
+    grok.implements(z3c.schema2xml.IXMLGenerator)
+
+    def output(self, container, value):
+        element = etree.SubElement(container, self.context.__name__)
+        field = self.context.value_type
+        if value is not None:
+            for v in value:
+                IXMLGenerator(field).output(container, v)
+
+    def input(self, element):
+        field = self.context.value_type
+        return [
+            IXMLGenerator(field).input(sub_element)
+            for sub_element in element]
+



More information about the Checkins mailing list