[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/form/browser/ Fixed
a bug: now the SequenceWidget displays errors on the
subwidgets if they
Albertas Agejevas
alga at pov.lt
Fri Mar 3 16:51:59 EST 2006
Log message for revision 65781:
Fixed a bug: now the SequenceWidget displays errors on the subwidgets if they
happen.
Changed:
U Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
-=-
Modified: Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2006-03-03 21:31:58 UTC (rev 65780)
+++ Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2006-03-03 21:51:58 UTC (rev 65781)
@@ -28,6 +28,7 @@
from zope.app.form.browser.widget import DisplayWidget, renderElement
from zope.app.i18n import ZopeMessageFactory as _
+
class SequenceWidget(BrowserWidget, InputWidget):
"""A widget baseclass for a sequence of fields.
@@ -41,12 +42,14 @@
def __init__(self, context, field, request, subwidget=None):
super(SequenceWidget, self).__init__(context, request)
-
self.subwidget = subwidget
+ # The subwidgets are cached in this dict if preserve_widgets is True.
+ self._widgets = {}
+ self.preserve_widgets = False
+
def __call__(self):
- """Render the widget
- """
+ """Render the widget"""
assert self.context.value_type is not None
render = []
@@ -65,11 +68,15 @@
if num_items > min_length:
render.append(
'<input class="editcheck" type="checkbox" '
- 'name="%s.remove_%d" />' %(self.name, i)
+ 'name="%s.remove_%d" />\n' % (self.name, i)
)
widget = self._getWidget(i)
widget.setRenderedValue(value)
- render.append(widget() + '</td></tr>')
+ error = widget.error()
+ if error:
+ render.append(error)
+ render.append('\n')
+ render.append(widget() + '</td></tr>\n')
# possibly generate the "remove" and "add" buttons
buttons = ''
@@ -85,25 +92,40 @@
button_label = translate(button_label, context=self.request,
default=button_label)
button_label = button_label % (field.title or field.__name__)
- buttons += '<input type="submit" name="%s.add" value="%s" />' % (
+ buttons += '<input type="submit" name="%s.add" value="%s" />\n' % (
self.name, button_label)
if buttons:
- render.append('<tr><td>%s</td></tr>' % buttons)
+ render.append('<tr><td>%s</td></tr>\n' % buttons)
- return ('<table border="0">%s</table>\n%s'
+ return ('<table border="0">\n%s</table>\n%s'
% (''.join(render), self._getPresenceMarker(num_items)))
def _getWidget(self, i):
- field = self.context.value_type
- if self.subwidget is not None:
- widget = self.subwidget(field, self.request)
- else:
- widget = zapi.getMultiAdapter((field, self.request), IInputWidget)
- widget.setPrefix('%s.%d.'%(self.name, i))
- return widget
+ """Return a widget for the i-th number of the sequence.
+ Normally this method creates a new widget each time, but when
+ the ``preserve_widgets`` attribute is True, it starts caching
+ widgets. We need it so that the errors on the subwidgets
+ would appear only if ``getInputValue`` was called.
+
+ ``getInputValue`` on the subwidgets gets called on each
+ request that has data.
+ """
+ if i not in self._widgets:
+ field = self.context.value_type
+ if self.subwidget is not None:
+ widget = self.subwidget(field, self.request)
+ else:
+ widget = zapi.getMultiAdapter((field, self.request),
+ IInputWidget)
+ widget.setPrefix('%s.%d.' % (self.name, i))
+ if not self.preserve_widgets:
+ return widget
+ self._widgets[i] = widget
+ return self._widgets[i]
+
def hidden(self):
- '''Render the list as hidden fields.'''
+ """Render the list as hidden fields."""
# length of sequence info
sequence = self._getRenderedValue()
num_items = len(sequence)
@@ -118,6 +140,7 @@
return "\n".join(parts)
def _getRenderedValue(self):
+ """Returns a sequence from the request or _data"""
if self._renderedValueSet():
sequence = list(self._data)
elif self.hasInput():
@@ -148,6 +171,7 @@
errors encountered, inputting, converting, or validating the data.
"""
if self.hasInput():
+ self.preserve_widgets = True
sequence = self._type(self._generateSequence())
if sequence != self.context.missing_value:
self.context.validate(sequence)
@@ -175,8 +199,10 @@
return (self.name + ".count") in self.request.form
def _generateSequence(self):
- """Take sequence info in the self.request and _data.
+ """Extract the values of the subwidgets from the request.
+ Returns a list of values.
+
This can only be called if self.hasInput() returns true.
"""
if self.context.value_type is None:
@@ -214,9 +240,11 @@
return sequence
+
class TupleSequenceWidget(SequenceWidget):
_type = tuple
+
class ListSequenceWidget(SequenceWidget):
_type = list
@@ -278,5 +306,5 @@
else:
widget = zapi.getMultiAdapter(
(field, self.request), IDisplayWidget)
- widget.setPrefix('%s.%d.'%(self.name, i))
+ widget.setPrefix('%s.%d.' % (self.name, i))
return widget
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2006-03-03 21:31:58 UTC (rev 65780)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2006-03-03 21:51:58 UTC (rev 65781)
@@ -24,14 +24,17 @@
from zope.interface.verify import verifyClass
from zope.app import zapi
-from zope.app.testing import ztapi
+from zope.app.testing import ztapi, setup
from zope.app.form.browser import TextWidget, ObjectWidget, DisplayWidget
from zope.app.form.browser import TupleSequenceWidget, ListSequenceWidget
from zope.app.form.browser import SequenceDisplayWidget
from zope.app.form.browser import SequenceWidget
from zope.app.form.interfaces import IDisplayWidget
from zope.app.form.interfaces import IInputWidget, MissingInputError
+from zope.app.form.interfaces import IWidgetInputError
+from zope.app.form.browser.interfaces import IWidgetInputErrorView
from zope.app.form import CustomWidgetFactory
+from zope.app.form.browser.exception import WidgetInputErrorView
from zope.app.form.browser.tests.support import VerifyResults
from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
@@ -79,6 +82,8 @@
def setUp(self):
super(SequenceWidgetTest, self).setUp()
ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+ ztapi.browserViewProviding(IWidgetInputError, WidgetInputErrorView,
+ IWidgetInputErrorView)
def test_haveNoData(self):
self.failIf(self._widget.hasInput())
@@ -280,7 +285,46 @@
data = widget._generateSequence()
self.assertEquals(data, [None, u'nonempty'])
+ def doctest_widgeterrors(self):
+ """Test that errors on subwidgets appear
+ >>> field = Tuple(__name__=u'foo',
+ ... value_type=TextLine(__name__='bar'))
+ >>> request = TestRequest(form={
+ ... 'field.foo.0.bar': u'',
+ ... 'field.foo.1.bar': u'nonempty',
+ ... 'field.foo.count': u'2'})
+ >>> widget = TupleSequenceWidget(field, field.value_type, request)
+
+ If we render the widget, we see no errors:
+
+ >>> print widget()
+ <BLANKLINE>
+ ...
+ <tr><td><input class="editcheck" type="checkbox"
+ name="field.foo.remove_0" />
+ <input class="textType" id="field.foo.0.bar" name="field.foo.0.bar"
+ size="20" type="text" value="" /></td></tr>
+ ...
+
+ However, if we call getInputValue or hasValidInput, the
+ errors on the widgets are preserved and displayed:
+
+ >>> widget.hasValidInput()
+ False
+
+ >>> print widget()
+ <BLANKLINE>
+ ...
+ <tr><td><input class="editcheck" type="checkbox"
+ name="field.foo.remove_0" />
+ <span class="error">Required input is missing.</span>
+ <input class="textType" id="field.foo.0.bar" name="field.foo.0.bar"
+ size="20" type="text" value="" /></td></tr>
+ ...
+ """
+
+
class SequenceDisplayWidgetTest(
VerifyResults, SequenceWidgetTestHelper, unittest.TestCase):
@@ -345,10 +389,24 @@
return super(UppercaseDisplayWidget, self).__call__().upper()
+def setUp(test):
+ setup.placelessSetUp()
+ ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+ ztapi.browserViewProviding(IWidgetInputError, WidgetInputErrorView,
+ IWidgetInputErrorView)
+
+
+def tearDown(test):
+ setup.placelessTearDown()
+
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(SequenceWidgetTest),
- doctest.DocTestSuite(),
+ doctest.DocTestSuite(setUp=setUp, tearDown=tearDown,
+ optionflags=doctest.ELLIPSIS
+ |doctest.NORMALIZE_WHITESPACE
+ |doctest.REPORT_NDIFF),
unittest.makeSuite(SequenceDisplayWidgetTest),
))
More information about the Zope3-Checkins
mailing list