[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