[Checkins] SVN: plone.z3cform/trunk/ * Ignore form.widgets. if ++widget++ path begins with it.

Laurence Rowe l at lrowe.co.uk
Fri Apr 29 11:20:42 EDT 2011


Log message for revision 121475:
  * Ignore form.widgets. if ++widget++ path begins with it.
    [lentinj]
  
  * Rework traverser to handle lists and subforms
    [lentinj]
  

Changed:
  U   plone.z3cform/trunk/docs/HISTORY.txt
  U   plone.z3cform/trunk/plone/z3cform/traversal.py
  U   plone.z3cform/trunk/plone/z3cform/traversal.txt

-=-
Modified: plone.z3cform/trunk/docs/HISTORY.txt
===================================================================
--- plone.z3cform/trunk/docs/HISTORY.txt	2011-04-29 07:04:35 UTC (rev 121474)
+++ plone.z3cform/trunk/docs/HISTORY.txt	2011-04-29 15:20:41 UTC (rev 121475)
@@ -4,6 +4,12 @@
 0.7.4 - unreleased
 ------------------
 
+* Ignore "form.widgets." if ++widget++ path begins with it.
+  [lentinj]
+
+* Rework traverser to handle lists and subforms
+  [lentinj]
+
 * Only search a group's widgets if they exist. collective.z3cform.wizard doesn't
   create widgets for pages/groups other than the current one
   [lentinj, elro]

Modified: plone.z3cform/trunk/plone/z3cform/traversal.py
===================================================================
--- plone.z3cform/trunk/plone/z3cform/traversal.py	2011-04-29 07:04:35 UTC (rev 121474)
+++ plone.z3cform/trunk/plone/z3cform/traversal.py	2011-04-29 15:20:41 UTC (rev 121475)
@@ -9,6 +9,7 @@
 from zope.publisher.interfaces.browser import IBrowserRequest
 
 from z3c.form.interfaces import IForm
+from z3c.form import util
 
 from plone.z3cform.interfaces import IFormWrapper
 from plone.z3cform.interfaces import IDeferSecurityCheck
@@ -59,24 +60,62 @@
         form.update()
         noLongerProvides(self.request, IDeferSecurityCheck)
 
-        # Find the widget - it may be in a group
-        widget = None
-        if name in form.widgets:
-            widget = form.widgets.get(name)
-        elif getattr(aq_base(form), 'groups', None) is not None:
-            for group in form.groups:
-                if group.widgets and name in group.widgets:
-                    widget = group.widgets.get(name)
+        # If name begins with form.widgets., remove it
+        form_widgets_prefix = util.expandPrefix(form.prefix)+util.expandPrefix(form.widgets.prefix)
+        if name.startswith(form_widgets_prefix):
+            name = name[len(form_widgets_prefix):]
 
+        # Split string up into dotted segments and work through
+        target = aq_base(form)
+        parts = name.split('.')
+        while len(parts) > 0:
+            part = parts.pop(0)
+            if type(getattr(target,'widgets',None)) is list: # i.e. a z3c.form.widget.MultiWidget
+                try:
+                    target = target.widgets[int(part)]
+                except IndexError:
+                    raise TraversalError("'"+part+"' not in range")
+                except ValueError:
+                    raise TraversalError("'"+part+"' not valid index")
+            elif hasattr(target,'widgets'): # Either base form, or subform
+                # Check to see if we can find a "Behaviour.widget"
+                new_target = self._form_traverse(target,part+'.'+parts[0]) if len(parts) > 0 else None
+
+                if new_target is not None:
+                    # Remove widget name from stack too
+                    parts.pop(0)
+                else:
+                    # Find widget in form without behaviour prefix
+                    new_target = self._form_traverse(target,part)
+
+                target = new_target
+            elif hasattr(target,'subform'): # subform-containing widget, only option is to go into subform
+                target = target.subform if part=='widgets' else None
+            else:
+               raise TraversalError('Cannot traverse through '+target.__repr__())
+
+            # Could not traverse from target to part
+            if target is None: raise TraversalError(part)
+
         # Make the parent of the widget the traversal parent.
         # This is required for security to work in Zope 2.12
-        if widget is not None:
-            widget.__parent__ = aq_inner(self.context)
-            return widget
-
+        if target is not None:
+            target.__parent__ = aq_inner(self.context)
+            return target
         raise TraversalError(name)
 
+    # Look for name within a form
+    def _form_traverse(self,form,name):
+        if name in form.widgets:
+            return form.widgets.get(name)
+        # If there are no groups, give up now
+        if getattr(aq_base(form), 'groups', None) is None:
+            return None
+        for group in form.groups:
+            if group.widgets and name in group.widgets:
+                return group.widgets.get(name)
 
+
 class WrapperWidgetTraversal(FormWidgetTraversal):
     """Allow traversal to widgets via the ++widget++ namespace. The context
     is the from layout wrapper.

Modified: plone.z3cform/trunk/plone/z3cform/traversal.txt
===================================================================
--- plone.z3cform/trunk/plone/z3cform/traversal.txt	2011-04-29 07:04:35 UTC (rev 121474)
+++ plone.z3cform/trunk/plone/z3cform/traversal.txt	2011-04-29 15:20:41 UTC (rev 121475)
@@ -33,13 +33,6 @@
     >>> from zope.annotation.interfaces import IAttributeAnnotatable
     >>> from z3c.form.interfaces import IFormLayer
 
-    >>> def make_request(form={}):
-    ...     request = TestRequest()
-    ...     request.form.update(form)
-    ...     alsoProvides(request, IFormLayer)
-    ...     alsoProvides(request, IAttributeAnnotatable)
-    ...     return request
-
     >>> from zope import interface, schema
     >>> from z3c.form import form, field, button
 
@@ -51,7 +44,6 @@
     >>> class MyForm(form.Form):
     ...     implements(IFieldsForm)
     ...     fields = field.Fields(MySchema)
-    ...     ignoreContext = True # don't use context to get widget data
     ...     
     ...     def update(self):
     ...         print "Updating test form"
@@ -69,26 +61,188 @@
     >>> from Acquisition import Implicit
     >>> class Bar(Implicit):
     ...     __allow_access_to_unprotected_subobjects__ = 1
-    ...     implements(Interface)
+    ...     implements(Interface,MySchema)
+    ...     age = 48
 
+And define a helper to make a request and traverse to ++widget++
+
     >>> from zope.component import getMultiAdapter
-    >>> context = Bar()
-    >>> request = make_request()
+    >>> from zope.traversing.interfaces import ITraversable
+    >>> def get_traverser(context,form_name,traverser_name=u"widget",form={}):
+    ...     request = TestRequest(form=form)
+    ...     request.form.update(form)
+    ...     alsoProvides(request, IFormLayer)
+    ...     alsoProvides(request, IAttributeAnnotatable)
+    ...     form = getMultiAdapter((context, request), name=form_name)
+    ...     return getMultiAdapter((form, request), name=traverser_name)
 
 Now, let's emulate the publisher and look up the namespace traversal
 adapter. For example, let's say we'd traversed to
 ../@@test-form/++widget++age. The publisher would then do:
 
-    >>> form = getMultiAdapter((context, request), name=u"test-form")
+    >>> traverser = get_traverser(Bar(),u"test-form")
+    >>> age_widget = traverser.traverse('age', [])
+    Updating test form
+    >>> age_widget
+    <TextWidget 'form.widgets.age'>
+    >>> age_widget.value
+    u'48'
 
-    >>> from zope.traversing.interfaces import ITraversable
-    >>> traverser = getMultiAdapter((form, request), name=u"widget")
-    >>> traverser.traverse('age', [])
+Can also specify form.widgets.age and get the same result.
+
+    >>> traverser = get_traverser(Bar(),u"test-form")
+    >>> age_widget = traverser.traverse('age', [])
     Updating test form
+    >>> age_widget
     <TextWidget 'form.widgets.age'>
+    >>> age_widget.value
+    u'48'
 
 Please note that this point, the form has been updated, but not rendered.
 
+Traversing through lists of widgets
+----------------
+
+Create another schema that contains a list of ages, and a context that
+contains some values for it.
+
+    >>> from z3c.form import form
+    >>> class MyListSchema(interface.Interface):
+    ...     list_field = schema.List(__name__='list_field',title=u"Object Field",value_type=schema.Int(title=u"Age"))
+
+    >>> class MyListForm(form.Form):
+    ...     implements(IFieldsForm)
+    ...     fields = field.Fields(MyListSchema)
+    ...     
+    ...     def update(self):
+    ...         print "Updating test form"
+    ...         super(MyListForm, self).update()
+
+    >>> provideAdapter(adapts=(Interface, IBrowserRequest),
+    ...                provides=Interface,
+    ...                factory=MyListForm,
+    ...                name=u"test-list-form")
+
+    >>> class Bar(Implicit):
+    ...     __allow_access_to_unprotected_subobjects__ = 1
+    ...     implements(Interface, MyListSchema)
+    ...     list_field = [48,49,50]
+
+Make a request. We should be able to find the list_field
+
+    >>> traverser = get_traverser(Bar(),u"test-list-form")
+    >>> list_widget = traverser.traverse('list_field', [])
+    Updating test form
+    >>> list_widget
+    <MultiWidget 'form.widgets.list_field'>
+
+And traverse through to individual items.
+
+    >>> traverser = get_traverser(Bar(),u"test-list-form")
+    >>> age_widget = traverser.traverse('list_field.1', [])
+    Updating test form
+    >>> age_widget
+    <TextWidget 'form.widgets.list_field.1'>
+    >>> age_widget.value
+    u'49'
+
+    >>> traverser = get_traverser(Bar(),u"test-list-form")
+    >>> age_widget = traverser.traverse('list_field.2', [])
+    Updating test form
+    >>> age_widget
+    <TextWidget 'form.widgets.list_field.2'>
+    >>> age_widget.value
+    u'50'
+
+Out of range errors are LocationErrors
+
+    >>> traverser = get_traverser(Bar(),u"test-list-form")
+    >>> age_widget = traverser.traverse('list_field.9', [])
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+    LocationError: "'9' not in range"
+
+Non-integer values are also LocationErrors
+
+    >>> traverser = get_traverser(Bar(),u"test-list-form")
+    >>> age_widget = traverser.traverse('list_field.camel', [])
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+    LocationError: "'camel' not valid index"
+
+Traversing object types
+-----------------------
+
+Now create a schema that contains an object with a list of ages within it
+
+    >>> from z3c.form import form
+    >>> class MyParentSchema(interface.Interface):
+    ...     obj_field = schema.Object(__name__='obj_field',title=u"Object Field",schema=MyListSchema)
+
+    >>> class MyParentForm(form.Form):
+    ...     implements(IFieldsForm)
+    ...     fields = field.Fields(MyParentSchema)
+    ...     
+    ...     def update(self):
+    ...         print "Updating test form"
+    ...         super(MyParentForm, self).update()
+
+    >>> provideAdapter(adapts=(Interface, IBrowserRequest),
+    ...                provides=Interface,
+    ...                factory=MyParentForm,
+    ...                name=u"test-parent-form")
+
+    >>> class Baz(Implicit):
+    ...     __allow_access_to_unprotected_subobjects__ = 1
+    ...     implements(Interface, MyListSchema)
+    ...     list_field = [48,49,50]
+
+    >>> class Bar(Implicit):
+    ...     __allow_access_to_unprotected_subobjects__ = 1
+    ...     implements(Interface, MyParentSchema)
+    ...     obj_field = Baz()
+
+
+We can find this object widget
+
+    >>> traverser = get_traverser(Bar(),u"test-parent-form")
+    >>> obj_widget = traverser.traverse('obj_field', [])
+    Updating test form
+    >>> obj_widget
+    <ObjectWidget 'form.widgets.obj_field'>
+
+And traverse through to the form within
+
+    >>> traverser = get_traverser(Bar(),u"test-parent-form")
+    >>> list_widget = traverser.traverse('obj_field.widgets.list_field', [])
+    Updating test form
+    >>> list_widget
+    <MultiWidget 'form.widgets.obj_field.widgets.list_field'>
+
+    >>> traverser = get_traverser(Bar(),u"test-parent-form")
+    >>> age_widget = traverser.traverse('obj_field.widgets.list_field.0', [])
+    Updating test form
+    >>> age_widget
+    <TextWidget 'form.widgets.obj_field.widgets.list_field.0'>
+    >>> age_widget.value
+    u'48'
+
+Missing widgets are LocationErrors
+
+    >>> traverser = get_traverser(Bar(),u"test-parent-form")
+    >>> list_widget = traverser.traverse('obj_field.widgets.camel_field', [])
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+    LocationError: 'camel_field'
+
+Looking for anything other than 'widgets' is also an error
+
+    >>> traverser = get_traverser(Bar(),u"test-parent-form")
+    >>> list_widget = traverser.traverse('obj_field.wodgets', [])
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+    LocationError: 'wodgets'
+
 Traversal on a layout wrapper view
 -----------------------------------
 



More information about the checkins mailing list