[Checkins] SVN: z3c.form/trunk/src/z3c/form/ a bunch of adjustments to make multiwidget and objectwidget happier together
Adam Groszer
agroszer at gmail.com
Mon Dec 1 11:50:04 EST 2008
Log message for revision 93492:
a bunch of adjustments to make multiwidget and objectwidget happier together
Changed:
U z3c.form/trunk/src/z3c/form/browser/multi.py
U z3c.form/trunk/src/z3c/form/browser/multi.txt
U z3c.form/trunk/src/z3c/form/browser/object.txt
A z3c.form/trunk/src/z3c/form/browser/objectmulti.txt
U z3c.form/trunk/src/z3c/form/browser/tests.py
U z3c.form/trunk/src/z3c/form/converter.py
U z3c.form/trunk/src/z3c/form/object.py
-=-
Modified: z3c.form/trunk/src/z3c/form/browser/multi.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/multi.py 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/browser/multi.py 2008-12-01 16:50:04 UTC (rev 93492)
@@ -60,15 +60,8 @@
@button.buttonAndHandler(_('Remove'), name='remove')
def handleRemove(self, action):
- ids = []
- append = ids.append
- counter = int(self.request.get(self.counterName, 0))
- for widget in self.widgets:
- name = '%s.remove' % (widget.name)
- if name in self.request:
- append(widget.id)
self.widgets = [widget for widget in self.widgets
- if widget.id not in ids]
+ if ('%s.remove' % (widget.name)) not in self.request]
@zope.interface.implementer(interfaces.IFieldWidget)
def multiFieldWidgetFactory(field, request):
Modified: z3c.form/trunk/src/z3c/form/browser/multi.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/multi.txt 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/browser/multi.txt 2008-12-01 16:50:04 UTC (rev 93492)
@@ -194,6 +194,10 @@
... 'widget.name.1':u'43',
... 'widget.buttons.add':'Add'})
>>> widget.update()
+
+ >>> widget.extract()
+ [u'42', u'43']
+
>>> print widget.render()
<div class="multi-widget">
<div id="widget-id-0-row" class="row">
@@ -274,6 +278,10 @@
... 'widget.name.1':u'43',
... 'widget.name.2':u'44'})
>>> widget.update()
+
+ >>> widget.extract()
+ [u'42', u'43', u'44']
+
>>> print widget.render()
<div class="multi-widget">
<div id="widget-id-0-row" class="row">
@@ -358,6 +366,12 @@
... 'widget.name.1.remove':u'1',
... 'widget.buttons.remove':'Remove'})
>>> widget.update()
+
+This is good so, because the Remove is an widget-internal submit action
+
+ >>> widget.extract()
+ [u'42', u'43', u'44']
+
>>> print widget.render()
<div class="multi-widget">
<div id="widget-id-0-row" class="row">
@@ -423,6 +437,10 @@
... 'widget.name.0':u'42',
... 'widget.name.1':u'bad'})
>>> widget.update()
+
+ >>> widget.extract()
+ [u'42', u'bad']
+
>>> print widget.render()
<div class="multi-widget">
<div id="widget-id-0-row" class="row">
Modified: z3c.form/trunk/src/z3c/form/browser/object.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/object.txt 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/browser/object.txt 2008-12-01 16:50:04 UTC (rev 93492)
@@ -38,7 +38,10 @@
>>> from z3c.form.error import MultipleErrorViewSnippet
>>> zope.component.provideAdapter(MultipleErrorViewSnippet)
+ >>> import z3c.form.object
+ >>> zope.component.provideAdapter(z3c.form.object.ObjectConverter)
+
As for all widgets, the objectwidget must provide the new ``IWidget``
interface:
@@ -150,7 +153,7 @@
>>> widget.ignoreContext = False
- >>> widget.value = v
+ >>> widget.value = dict(foofield=42, barfield=666)
>>> widget.update()
@@ -250,7 +253,25 @@
'ThisMustStayTheSame'
-HMMMM.... do we to test error handling here?
+ >>> converter = interfaces.IDataConverter(widget)
+
+ >>> value = converter.toFieldValue(wv)
+ >>> value
+ <z3c.form.testing.MySubObject object at ...>
+ >>> value.foofield
+ 2
+ >>> value.barfield
+ 999
+
+This is a different object:
+
+ >>> value.__marker__
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'MySubObject' object has no attribute '__marker__'
+
+
+HMMMM.... do we have to test error handling here?
I'm tempted to leave it out as no widgets seem to do this.
#Error handling is next. Let's use the value "bad" (an invalid integer literal)
@@ -326,8 +347,6 @@
We have to provide an adapter first:
>>> zope.component.provideAdapter(object.ObjectFieldWidget)
- >>> import z3c.form.object
- >>> zope.component.provideAdapter(z3c.form.object.ObjectConverter)
Forms and our objectwidget fire events on add and edit, setup a subscriber
for those:
@@ -680,7 +699,7 @@
>>> editform.update()
-Until we have updated the form:
+Let's check what are ther esults of the update:
>>> root['first'].subobject.foofield
666
@@ -1066,13 +1085,8 @@
>>> request = TestRequest()
-#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
-
>>> myaddform = MyAddForm(root, request)
-#>>> from pub.dbgpclient import brk; brk('192.168.32.1')
-
-
>>> myaddform.update()
As usual, the form contains a widget manager with the expected widget
Added: z3c.form/trunk/src/z3c/form/browser/objectmulti.txt
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/objectmulti.txt (rev 0)
+++ z3c.form/trunk/src/z3c/form/browser/objectmulti.txt 2008-12-01 16:50:04 UTC (rev 93492)
@@ -0,0 +1,936 @@
+===================
+Multi+Object Widget
+===================
+
+The multi widget allows you to add and edit one or more values.
+
+In order to not overwhelm you with our set of well-chosen defaults,
+all the default component registrations have been made prior to doing those
+examples:
+
+ >>> from z3c.form import testing
+ >>> testing.setupFormDefaults()
+
+As for all widgets, the multi widget must provide the new ``IWidget``
+interface:
+
+ >>> from zope.interface.verify import verifyClass
+ >>> from z3c.form import interfaces
+ >>> from z3c.form.browser import multi
+
+ >>> verifyClass(interfaces.IWidget, multi.MultiWidget)
+ True
+
+The widget can be instantiated only using the request:
+
+ >>> from z3c.form.testing import TestRequest
+ >>> request = TestRequest()
+ >>> widget = multi.MultiWidget(request)
+
+Before rendering the widget, one has to set the name and id of the widget:
+
+ >>> widget.id = 'widget-id'
+ >>> widget.name = 'widget.name'
+
+We also need to register the template for at least the widget and request:
+
+ >>> import zope.component
+ >>> from zope.pagetemplate.interfaces import IPageTemplate
+ >>> from z3c.form.testing import getPath
+ >>> from z3c.form.widget import WidgetTemplateFactory
+
+ >>> zope.component.provideAdapter(
+ ... WidgetTemplateFactory(getPath('multi_input.pt'), 'text/html'),
+ ... (None, None, None, None, interfaces.IMultiWidget),
+ ... IPageTemplate, name=interfaces.INPUT_MODE)
+
+For the next test, we need to setup our button handler adapters.
+
+ >>> from z3c.form import button
+ >>> zope.component.provideAdapter(button.ButtonActions)
+ >>> zope.component.provideAdapter(button.ButtonActionHandler)
+ >>> zope.component.provideAdapter(button.ButtonAction,
+ ... provides=interfaces.IButtonAction)
+
+Our submit buttons will need a template as well:
+
+ >>> zope.component.provideAdapter(
+ ... WidgetTemplateFactory(getPath('submit_input.pt'), 'text/html'),
+ ... (None, None, None, None, interfaces.ISubmitWidget),
+ ... IPageTemplate, name=interfaces.INPUT_MODE)
+
+We can now render the widget:
+
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget">
+ <div class="buttons">
+ <input type="submit" id="widget-buttons-add"
+ name="widget.buttons.add"
+ class="submit-widget button-field" value="Add" />
+ <input type="submit" id="widget-buttons-remove"
+ name="widget.buttons.remove"
+ class="submit-widget button-field" value="Remove" />
+ </div>
+ </div>
+ <input type="hidden" name="widget.name.count" value="0" />
+
+As you can see the widget is empty and doesn't provide values. This is because
+the widget does not know what sub-widgets to display. So let's register a
+`IFieldWidget` adapter and a template for our `IInt` field:
+
+ >>> import z3c.form.interfaces
+ >>> from z3c.form.browser.text import TextFieldWidget
+ >>> zope.component.provideAdapter(TextFieldWidget,
+ ... (zope.schema.interfaces.IInt, z3c.form.interfaces.IFormLayer))
+
+ >>> zope.component.provideAdapter(
+ ... WidgetTemplateFactory(getPath('text_input.pt'), 'text/html'),
+ ... (None, None, None, None, interfaces.ITextWidget),
+ ... IPageTemplate, name=interfaces.INPUT_MODE)
+
+Let's now update the widget and check it again.
+
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget">
+ <div class="buttons">
+ <input type="submit" id="widget-buttons-add"
+ name="widget.buttons.add"
+ class="submit-widget button-field" value="Add" />
+ <input type="submit" id="widget-buttons-remove"
+ name="widget.buttons.remove"
+ class="submit-widget button-field" value="Remove" />
+ </div>
+ </div>
+ <input type="hidden" name="widget.name.count" value="0" />
+
+It's still the same. Since the widget doesn't provide a field nothing useful
+gets rendered. Now let's define a field for this widget and check it again:
+
+ >>> from z3c.form.widget import FieldWidget
+
+ >>> from z3c.form.testing import IMySubObject
+ >>> from z3c.form.testing import IMySecond
+ >>> from z3c.form.testing import MySubObject
+ >>> from z3c.form.testing import MySecond
+
+ >>> from z3c.form.object import registerFactoryAdapter
+ >>> registerFactoryAdapter(IMySubObject, MySubObject)
+
+ >>> field = zope.schema.List(
+ ... __name__='foo',
+ ... value_type=zope.schema.Object(title=u'my object widget',
+ ... schema=IMySubObject),
+ ... )
+
+ >>> widget = FieldWidget(field, widget)
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="buttons">
+ <input type="submit" id="widget-buttons-add"
+ name="widget.buttons.add"
+ class="submit-widget button-field" value="Add" />
+ <input type="submit" id="widget-buttons-remove"
+ name="widget.buttons.remove"
+ class="submit-widget button-field" value="Remove" />
+ </div>
+ </div>
+ <input type="hidden" name="foo.count" value="0" />
+
+As you can see, there is still no input value. Let's provide some values for
+this widget. Before we can do that, we will need to register a data converter
+for our multi widget and the data converter dispatcher adapter:
+
+ >>> from z3c.form.converter import IntegerDataConverter
+ >>> from z3c.form.converter import FieldWidgetDataConverter
+ >>> from z3c.form.converter import MultiConverter
+ >>> from z3c.form.validator import SimpleFieldValidator
+ >>> zope.component.provideAdapter(IntegerDataConverter)
+ >>> zope.component.provideAdapter(FieldWidgetDataConverter)
+ >>> zope.component.provideAdapter(SimpleFieldValidator)
+ >>> zope.component.provideAdapter(MultiConverter)
+
+Bunch of adapters to get objectwidget work:
+
+ >>> from z3c.form import datamanager
+ >>> zope.component.provideAdapter(datamanager.DictionaryField)
+
+ >>> import z3c.form.browser.object
+ >>> zope.component.provideAdapter(z3c.form.browser.object.ObjectFieldWidget)
+ >>> import z3c.form.object
+ >>> zope.component.provideAdapter(z3c.form.object.ObjectConverter)
+ >>> import z3c.form.error
+ >>> zope.component.provideAdapter(z3c.form.error.ValueErrorViewSnippet)
+ >>> from z3c.form.object import SubformAdapter
+ >>> zope.component.provideAdapter(SubformAdapter)
+
+ >>> from zope.pagetemplate.interfaces import IPageTemplate
+ >>> from z3c.form.testing import getPath
+ >>> from z3c.form.widget import WidgetTemplateFactory
+
+ >>> zope.component.provideAdapter(
+ ... WidgetTemplateFactory(getPath('object_input.pt'), 'text/html'),
+ ... (None, None, None, None, interfaces.IObjectWidget),
+ ... IPageTemplate, name=interfaces.INPUT_MODE)
+
+ >>> zope.component.provideAdapter(
+ ... WidgetTemplateFactory(getPath('object_display.pt'), 'text/html'),
+ ... (None, None, None, None, interfaces.IObjectWidget),
+ ... IPageTemplate, name=interfaces.DISPLAY_MODE)
+
+ >>> widget.update()
+
+ >>> widget.value = [dict(foofield=42, barfield=666),
+ ... dict(foofield=789, barfield=321)]
+
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div id="foo-0-row" class="row">
+ <div class="label">
+ <label for="foo-0">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input id="foo-0-remove"
+ name="foo.0.remove"
+ class="multi-widget-checkbox checkbox-widget"
+ type="checkbox" value="1" />
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input id="foo-0-widgets-foofield"
+ name="foo.0.widgets.foofield"
+ class="text-widget required int-field" value="42"
+ type="text" />
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input id="foo-0-widgets-barfield"
+ name="foo.0.widgets.barfield"
+ class="text-widget int-field" value="666"
+ type="text" />
+ </div>
+ <input name="foo.0-empty-marker" type="hidden"
+ value="1" />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="foo-1-row" class="row">
+ <div class="label">
+ <label for="foo-1">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input id="foo-1-remove"
+ name="foo.1.remove"
+ class="multi-widget-checkbox checkbox-widget"
+ type="checkbox" value="1" />
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-1-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input id="foo-1-widgets-foofield"
+ name="foo.1.widgets.foofield"
+ class="text-widget required int-field"
+ value="789" type="text" />
+ </div>
+ <div class="label">
+ <label for="foo-1-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input id="foo-1-widgets-barfield"
+ name="foo.1.widgets.barfield"
+ class="text-widget int-field" value="321"
+ type="text" />
+ </div>
+ <input name="foo.1-empty-marker" type="hidden"
+ value="1" />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input id="widget-buttons-add" name="widget.buttons.add"
+ class="submit-widget button-field" value="Add"
+ type="submit" />
+ <input id="widget-buttons-remove"
+ name="widget.buttons.remove"
+ class="submit-widget button-field" value="Remove"
+ type="submit" />
+ </div>
+ </div>
+ <input type="hidden" name="foo.count" value="2" />
+
+Let's see what we get on value extraction:
+
+ >>> widget.extract()
+ <NOVALUE>
+
+If we now click on the ``Add`` button, we will get a new input field for enter
+a new value:
+
+ >>> widget.request = TestRequest(form={'foo.count':u'2',
+ ... 'foo.0.widgets.foofield':u'42',
+ ... 'foo.0.widgets.barfield':u'666',
+ ... 'foo.0-empty-marker':u'1',
+ ... 'foo.1.widgets.foofield':u'789',
+ ... 'foo.1.widgets.barfield':u'321',
+ ... 'foo.1-empty-marker':u'1',
+ ... 'widget.buttons.add':'Add'})
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="row" id="foo-0-row">
+ <div class="label">
+ <label for="foo-0">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget"
+ id="foo-0-remove"
+ name="foo.0.remove"
+ type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field"
+ id="foo-0-widgets-foofield"
+ name="foo.0.widgets.foofield"
+ type="text" value="42">
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field"
+ id="foo-0-widgets-barfield"
+ name="foo.0.widgets.barfield"
+ type="text" value="666">
+ </div>
+ <input name="foo.0-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-1-row">
+ <div class="label">
+ <label for="foo-1">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget"
+ id="foo-1-remove"
+ name="foo.1.remove"
+ type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-1-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field"
+ id="foo-1-widgets-foofield"
+ name="foo.1.widgets.foofield"
+ type="text" value="789">
+ </div>
+ <div class="label">
+ <label for="foo-1-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field"
+ id="foo-1-widgets-barfield"
+ name="foo.1.widgets.barfield"
+ type="text" value="321">
+ </div>
+ <input name="foo.1-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-2-row">
+ <div class="label">
+ <label for="foo-2">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget"
+ id="foo-2-remove"
+ name="foo.2.remove"
+ type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-2-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field"
+ id="foo-2-widgets-foofield"
+ name="foo.2.widgets.foofield"
+ type="text" value="1,111">
+ </div>
+ <div class="label">
+ <label for="foo-2-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field"
+ id="foo-2-widgets-barfield"
+ name="foo.2.widgets.barfield"
+ type="text" value="2,222">
+ </div>
+ <input name="foo.2-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input id="widget-buttons-add" name="widget.buttons.add"
+ class="submit-widget button-field" value="Add"
+ type="submit" />
+ <input id="widget-buttons-remove"
+ name="widget.buttons.remove"
+ class="submit-widget button-field" value="Remove"
+ type="submit" />
+ </div>
+ </div>
+ <input name="foo.count" type="hidden" value="3">
+
+Let's see what we get on value extraction:
+
+ >>> value = widget.extract()
+ >>> value
+ [{'foofield': 42, 'barfield': 666}, {'foofield': 789, 'barfield': 321}]
+ >>> converter = interfaces.IDataConverter(widget)
+
+ >>> value = converter.toFieldValue(value)
+ >>> value
+ [<z3c.form.testing.MySubObject object at ...>,
+ <z3c.form.testing.MySubObject object at ...>]
+
+ >>> value[0].foofield
+ 42
+ >>> value[0].barfield
+ 666
+
+
+Now let's store the new value:
+
+
+ >>> widget.request = TestRequest(form={'foo.count':u'3',
+ ... 'foo.0.widgets.foofield':u'42',
+ ... 'foo.0.widgets.barfield':u'666',
+ ... 'foo.0-empty-marker':u'1',
+ ... 'foo.1.widgets.foofield':u'789',
+ ... 'foo.1.widgets.barfield':u'321',
+ ... 'foo.1-empty-marker':u'1',
+ ... 'foo.2.widgets.foofield':u'46',
+ ... 'foo.2.widgets.barfield':u'98',
+ ... 'foo.2-empty-marker':u'1',
+ ... })
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="row" id="foo-0-row">
+ <div class="label">
+ <label for="foo-0">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-0-remove" name="foo.0.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-0-widgets-foofield" name="foo.0.widgets.foofield" type="text" value="42">
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-0-widgets-barfield" name="foo.0.widgets.barfield" type="text" value="666">
+ </div>
+ <input name="foo.0-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-1-row">
+ <div class="label">
+ <label for="foo-1">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-1-remove" name="foo.1.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-1-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-1-widgets-foofield" name="foo.1.widgets.foofield" type="text" value="789">
+ </div>
+ <div class="label">
+ <label for="foo-1-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-1-widgets-barfield" name="foo.1.widgets.barfield" type="text" value="321">
+ </div>
+ <input name="foo.1-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-2-row">
+ <div class="label">
+ <label for="foo-2">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-2-remove" name="foo.2.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-2-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-2-widgets-foofield" name="foo.2.widgets.foofield" type="text" value="46">
+ </div>
+ <div class="label">
+ <label for="foo-2-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-2-widgets-barfield" name="foo.2.widgets.barfield" type="text" value="98">
+ </div>
+ <input name="foo.2-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input class="submit-widget button-field" id="widget-buttons-add" name="widget.buttons.add" type="submit" value="Add">
+ <input class="submit-widget button-field" id="widget-buttons-remove" name="widget.buttons.remove" type="submit" value="Remove">
+ </div>
+ </div>
+ <input name="foo.count" type="hidden" value="3">
+
+Let's see what we get on value extraction:
+
+ >>> value = widget.extract()
+ >>> value
+ [{'foofield': 42, 'barfield': 666},
+ {'foofield': 789, 'barfield': 321},
+ {'foofield': 46, 'barfield': 98}]
+ >>> converter = interfaces.IDataConverter(widget)
+
+ >>> value = converter.toFieldValue(value)
+ >>> value
+ [<z3c.form.testing.MySubObject object at ...>,
+ <z3c.form.testing.MySubObject object at ...>]
+
+ >>> value[0].foofield
+ 42
+ >>> value[0].barfield
+ 666
+
+
+As you can see in the above sample, the new stored value gets rendered as a
+real value and the new adding value input field is gone. Now let's try to
+remove an existing value:
+
+ >>> widget.request = TestRequest(form={'foo.count':u'3',
+ ... 'foo.0.widgets.foofield':u'42',
+ ... 'foo.0.widgets.barfield':u'666',
+ ... 'foo.0-empty-marker':u'1',
+ ... 'foo.1.widgets.foofield':u'789',
+ ... 'foo.1.widgets.barfield':u'321',
+ ... 'foo.1-empty-marker':u'1',
+ ... 'foo.2.widgets.foofield':u'46',
+ ... 'foo.2.widgets.barfield':u'98',
+ ... 'foo.2-empty-marker':u'1',
+ ... 'foo.1.remove':u'1',
+ ... 'widget.buttons.remove':'Remove'})
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="row" id="foo-0-row">
+ <div class="label">
+ <label for="foo-0">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-0-remove" name="foo.0.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-0-widgets-foofield" name="foo.0.widgets.foofield" type="text" value="42">
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-0-widgets-barfield" name="foo.0.widgets.barfield" type="text" value="666">
+ </div>
+ <input name="foo.0-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-2-row">
+ <div class="label">
+ <label for="foo-2">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-2-remove" name="foo.2.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-2-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-2-widgets-foofield" name="foo.2.widgets.foofield" type="text" value="46">
+ </div>
+ <div class="label">
+ <label for="foo-2-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-2-widgets-barfield" name="foo.2.widgets.barfield" type="text" value="98">
+ </div>
+ <input name="foo.2-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input class="submit-widget button-field" id="widget-buttons-add" name="widget.buttons.add" type="submit" value="Add">
+ <input class="submit-widget button-field" id="widget-buttons-remove" name="widget.buttons.remove" type="submit" value="Remove">
+ </div>
+ </div>
+ <input name="foo.count" type="hidden" value="2">
+
+Let's see what we get on value extraction:
+(this is good so, because Remove is a widget-internal submit)
+
+ >>> value = widget.extract()
+ >>> value
+ [{'foofield': 42, 'barfield': 666},
+ {'foofield': 789, 'barfield': 321},
+ {'foofield': 46, 'barfield': 98}]
+ >>> converter = interfaces.IDataConverter(widget)
+
+ >>> value = converter.toFieldValue(value)
+ >>> value
+ [<z3c.form.testing.MySubObject object at ...>,
+ <z3c.form.testing.MySubObject object at ...>]
+
+ >>> value[0].foofield
+ 42
+ >>> value[0].barfield
+ 666
+
+
+Error handling is next. Let's use the value "bad" (an invalid integer literal)
+as input for our internal (sub) widget.
+
+ >>> from z3c.form.error import ErrorViewSnippet
+ >>> from z3c.form.error import StandardErrorViewTemplate
+ >>> zope.component.provideAdapter(ErrorViewSnippet)
+ >>> zope.component.provideAdapter(StandardErrorViewTemplate)
+
+ >>> widget.request = TestRequest(form={'foo.count':u'2',
+ ... 'foo.0.widgets.foofield':u'42',
+ ... 'foo.0.widgets.barfield':u'666',
+ ... 'foo.0-empty-marker':u'1',
+ ... 'foo.1.widgets.foofield':u'bad',
+ ... 'foo.1.widgets.barfield':u'98',
+ ... 'foo.1-empty-marker':u'1',
+ ... })
+
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="row" id="foo-0-row">
+ <div class="label">
+ <label for="foo-0">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-0-remove" name="foo.0.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-0-widgets-foofield" name="foo.0.widgets.foofield" type="text" value="42">
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-0-widgets-barfield" name="foo.0.widgets.barfield" type="text" value="666">
+ </div>
+ <input name="foo.0-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-1-row">
+ <div class="label">
+ <label for="foo-1">
+ <span>my object widget</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-1-remove" name="foo.1.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-1-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="error">
+ <div class="error">The entered value is not a valid integer literal.</div>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-1-widgets-foofield" name="foo.1.widgets.foofield" type="text" value="bad">
+ </div>
+ <div class="label">
+ <label for="foo-1-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-1-widgets-barfield" name="foo.1.widgets.barfield" type="text" value="98">
+ </div>
+ <input name="foo.1-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ <div class="error">
+ <div class="error">Object is of wrong type.</div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input class="submit-widget button-field" id="widget-buttons-add" name="widget.buttons.add" type="submit" value="Add">
+ <input class="submit-widget button-field" id="widget-buttons-remove" name="widget.buttons.remove" type="submit" value="Remove">
+ </div>
+ </div>
+ <input name="foo.count" type="hidden" value="2">
+
+Let's see what we get on value extraction:
+
+ >>> value = widget.extract()
+ >>> value
+ [{'foofield': 42, 'barfield': 666},
+ {'foofield': u'bad', 'barfield': u'98'}]
+
+
+Label
+-----
+
+There is an option which allows to disable the label for the (sub) widgets.
+You can set the `showLabel` option to `False` which will skip rendering the
+labels. Alternatively you can also register your own template for your layer
+if you like to skip the label rendering for all widgets.
+
+
+ >>> field = zope.schema.List(
+ ... __name__='foo',
+ ... value_type=zope.schema.Object(title=u'ignored_title',
+ ... schema=IMySubObject),
+ ... )
+ >>> request = TestRequest()
+ >>> widget = multi.MultiWidget(request)
+ >>> widget = FieldWidget(field, widget)
+ >>> widget.value = [dict(foofield=42, barfield=666),
+ ... dict(foofield=789, barfield=321)]
+ >>> widget.showLabel = False
+ >>> widget.update()
+ >>> print widget.render()
+ <div class="multi-widget required">
+ <div class="row" id="foo-0-row">
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-0-remove" name="foo.0.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-0-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-0-widgets-foofield" name="foo.0.widgets.foofield" type="text" value="42">
+ </div>
+ <div class="label">
+ <label for="foo-0-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-0-widgets-barfield" name="foo.0.widgets.barfield" type="text" value="666">
+ </div>
+ <input name="foo.0-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row" id="foo-1-row">
+ <div class="widget">
+ <div class="multi-widget-checkbox">
+ <input class="multi-widget-checkbox checkbox-widget" id="foo-1-remove" name="foo.1.remove" type="checkbox" value="1">
+ </div>
+ <div class="multi-widget-input">
+ <div class="object-widget required">
+ <div class="label">
+ <label for="foo-1-widgets-foofield">
+ <span>My foo field</span>
+ <span class="required">*</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget required int-field" id="foo-1-widgets-foofield" name="foo.1.widgets.foofield" type="text" value="789">
+ </div>
+ <div class="label">
+ <label for="foo-1-widgets-barfield">
+ <span>My dear bar</span>
+ </label>
+ </div>
+ <div class="widget">
+ <input class="text-widget int-field" id="foo-1-widgets-barfield" name="foo.1.widgets.barfield" type="text" value="321">
+ </div>
+ <input name="foo.1-empty-marker" type="hidden" value="1">
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="buttons">
+ <input class="submit-widget button-field" id="widget-buttons-add" name="widget.buttons.add" type="submit" value="Add">
+ <input class="submit-widget button-field" id="widget-buttons-remove" name="widget.buttons.remove" type="submit" value="Remove">
+ </div>
+ </div>
+ <input name="foo.count" type="hidden" value="2">
\ No newline at end of file
Property changes on: z3c.form/trunk/src/z3c/form/browser/objectmulti.txt
___________________________________________________________________
Added: svn:keywords
+ Date Author Id Revision
Added: svn:eol-style
+ native
Modified: z3c.form/trunk/src/z3c/form/browser/tests.py
===================================================================
--- z3c.form/trunk/src/z3c/form/browser/tests.py 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/browser/tests.py 2008-12-01 16:50:04 UTC (rev 93492)
@@ -96,6 +96,11 @@
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
checker=checker,
),
+ DocFileSuite('objectmulti.txt',
+ setUp=setUp, tearDown=testing.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ checker=checker,
+ ),
DocFileSuite('multi.txt',
setUp=setUp, tearDown=testing.tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
Modified: z3c.form/trunk/src/z3c/form/converter.py
===================================================================
--- z3c.form/trunk/src/z3c/form/converter.py 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/converter.py 2008-12-01 16:50:04 UTC (rev 93492)
@@ -341,13 +341,17 @@
"""Just dispatch it."""
if value is self.field.missing_value:
return []
- # We relay on the default registered widget, this is probably a
+ # We rely on the default registered widget, this is probably a
# restriction for custom widgets. If so use your own MultiWidget and
# register your own converter which will get the right widget for the
# used value_type.
field = self.field.value_type
widget = zope.component.getMultiAdapter((field, self.widget.request),
interfaces.IFieldWidget)
+ if interfaces.IFormAware.providedBy(self.widget):
+ #form property required by objecwidget
+ widget.form = self.widget.form
+ zope.interface.alsoProvides(widget, interfaces.IFormAware)
converter = zope.component.getMultiAdapter((field, widget),
interfaces.IDataConverter)
@@ -355,13 +359,24 @@
return [converter.toWidgetValue(v) for v in value]
def toFieldValue(self, value):
- """See interfaces.IDataConverter"""
- collectionType = self.field._type
+ """Just dispatch it."""
if not len(value):
return self.field.missing_value
- valueType = self.field.value_type._type
- values = [valueType(v) for v in value]
+
+ field = self.field.value_type
+ widget = zope.component.getMultiAdapter((field, self.widget.request),
+ interfaces.IFieldWidget)
+ if interfaces.IFormAware.providedBy(self.widget):
+ #form property required by objecwidget
+ widget.form = self.widget.form
+ zope.interface.alsoProvides(widget, interfaces.IFormAware)
+ converter = zope.component.getMultiAdapter((field, widget),
+ interfaces.IDataConverter)
+
+ values = [converter.toFieldValue(v) for v in value]
+
# convert the field values to a tuple or list
+ collectionType = self.field._type
return collectionType(values)
Modified: z3c.form/trunk/src/z3c/form/object.py
===================================================================
--- z3c.form/trunk/src/z3c/form/object.py 2008-12-01 16:36:39 UTC (rev 93491)
+++ z3c.form/trunk/src/z3c/form/object.py 2008-12-01 16:50:04 UTC (rev 93492)
@@ -143,21 +143,25 @@
return obj
def toFieldValue(self, value):
- """See interfaces.IDataConverter"""
+ """field value is an Object type, that provides field.schema"""
if value is interfaces.NOVALUE:
return self.field.missing_value
- if self.widget.subform.ignoreContext:
+ if self.widget.subform is None:
+ #creepy situation when the widget is hanging in nowhere
obj = self.createObject(value)
else:
- dm = zope.component.getMultiAdapter(
- (self.widget.context, self.field), interfaces.IDataManager)
- try:
- obj = dm.get()
- except KeyError:
+ if self.widget.subform.ignoreContext:
obj = self.createObject(value)
- except AttributeError:
- obj = self.createObject(value)
+ else:
+ dm = zope.component.getMultiAdapter(
+ (self.widget.context, self.field), interfaces.IDataManager)
+ try:
+ obj = dm.get()
+ except KeyError:
+ obj = self.createObject(value)
+ except AttributeError:
+ obj = self.createObject(value)
obj = self.field.schema(obj)
@@ -227,11 +231,14 @@
def value():
"""This invokes updateWidgets on any value change e.g. update/extract."""
def get(self):
- return self.extract(setErrors=True)
- #value = {}
- #for name in zope.schema.getFieldNames(self.field.schema):
- # value[name] = self.subform.widgets[name].value
- #return value
+ #value (get) cannot raise an exception, then we return insane values
+ try:
+ return self.extract(setErrors=True)
+ except MultipleErrors:
+ value = {}
+ for name in zope.schema.getFieldNames(self.field.schema):
+ value[name] = self.subform.widgets[name].value
+ return value
def set(self, value):
self._value = value
# ensure that we apply our new values to the widgets
@@ -341,8 +348,11 @@
"""Most basic-default object factory adapter"""
zope.interface.implements(interfaces.IObjectFactory)
- zope.component.adapts(zope.interface.Interface, interfaces.IFormLayer,
- interfaces.IForm, interfaces.IWidget)
+ zope.component.adapts(
+ zope.interface.Interface, #context
+ interfaces.IFormLayer, #request
+ zope.interface.Interface, #form -- but can become None easily (in tests)
+ interfaces.IWidget) #widget
factory = None
More information about the Checkins
mailing list