[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/form/browser/ make
the sequence input widget conform to the widget API
Fred L. Drake, Jr.
fdrake at gmail.com
Wed Jun 1 23:12:41 EDT 2005
Log message for revision 30596:
make the sequence input widget conform to the widget API
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 2005-06-01 22:14:18 UTC (rev 30595)
+++ Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2005-06-02 03:12:38 UTC (rev 30596)
@@ -23,6 +23,7 @@
from zope.app import zapi
from zope.app.form.interfaces import IInputWidget
+from zope.app.form.interfaces import WidgetInputError, MissingInputError
from zope.app.form import InputWidget
from zope.app.form.browser.widget import BrowserWidget
from zope.app.i18n import ZopeMessageIDFactory as _
@@ -37,7 +38,7 @@
implements(IInputWidget)
_type = tuple
- _data = () # pre-existing sequence items (from setRenderedValue)
+ _data = None # pre-existing sequence items (from setRenderedValue)
def __init__(self, context, field, request, subwidget=None):
super(SequenceWidget, self).__init__(context, request)
@@ -52,17 +53,11 @@
render = []
# length of sequence info
- sequence = list(self._generateSequence())
+ sequence = self._getRenderedValue()
num_items = len(sequence)
min_length = self.context.min_length
max_length = self.context.max_length
- # ensure minimum number of items in the form
- if num_items < min_length:
- for i in range(min_length - num_items):
- sequence.append(None)
- num_items = len(sequence)
-
# generate each widget from items in the sequence - adding a
# "remove" button for each one
for i in range(num_items):
@@ -80,12 +75,11 @@
# possibly generate the "remove" and "add" buttons
buttons = ''
if render and num_items > min_length:
- remove_botton_name = 'remove-selected-items-of-seq-' + self.name
button_label = _('remove-selected-items', "Remove selected items")
button_label = translate(button_label, context=self.request,
default=button_label)
- buttons += '<input type="submit" value="%s" name="%s"/>' % (
- button_label, remove_botton_name)
+ buttons += ('<input type="submit" value="%s" name="%s.remove"/>'
+ % (button_label, self.name))
if max_length is None or num_items < max_length:
field = self.context.value_type
button_label = _('Add %s')
@@ -97,7 +91,8 @@
if buttons:
render.append('<tr><td>%s</td></tr>' % buttons)
- return '<table border="0">' + ''.join(render) + '</table>'
+ return ('<table border="0">%s</table>\n%s'
+ % (''.join(render), self._getPresenceMarker(num_items)))
def _getWidget(self, i):
field = self.context.value_type
@@ -109,27 +104,41 @@
return widget
def hidden(self):
- ''' Render the list as hidden fields '''
+ '''Render the list as hidden fields.'''
# length of sequence info
- sequence = self._generateSequence()
+ sequence = self._getRenderedValue()
num_items = len(sequence)
- min_length = self.context.min_length
- # ensure minimum number of items in the form
- if num_items < min_length:
- for i in range(min_length - num_items):
- sequence.append(None)
- num_items = len(sequence)
-
# generate hidden fields for each value
- s = ''
+ parts = [self._getPresenceMarker(num_items)]
for i in range(num_items):
value = sequence[i]
widget = self._getWidget(i)
widget.setRenderedValue(value)
- s += widget.hidden()
- return s
+ parts.append(widget.hidden())
+ return "\n".join(parts)
+ def _getRenderedValue(self):
+ sequence = self._data
+ if sequence is None:
+ if self.hasInput():
+ sequence = list(self._generateSequence())
+ else:
+ sequence = []
+ # ensure minimum number of items in the form
+ if len(sequence) < self.context.min_length:
+ sequence = list(sequence)
+ for i in range(self.context.min_length - len(sequence)):
+ # Shouldn't this use self.field.value_type.missing_value,
+ # instead of None?
+ sequence.append(None)
+ sequence = self._type(sequence)
+ return sequence
+
+ def _getPresenceMarker(self, count=0):
+ return ('<input type="hidden" name="%s.count" value="%d" />'
+ % (self.name, count))
+
def getInputValue(self):
"""Return converted and validated widget data.
@@ -139,14 +148,18 @@
If there is no user input and the field is not required, then
the field default value will be returned.
- A ``WidgetInputError`` is returned in the case of one or more
+ A ``WidgetInputError`` is raised in the case of one or more
errors encountered, inputting, converting, or validating the data.
"""
- sequence = self._generateSequence()
- # validate the input values
- for value in sequence:
- self.context.value_type.validate(value)
- return self._type(sequence)
+ if self.hasInput():
+ sequence = self._type(self._generateSequence())
+ if sequence != self.context.missing_value:
+ self.context.validate(sequence)
+ elif self.context.required:
+ raise MissingInputError(self.context.__name__,
+ self.context.title)
+ return sequence
+ raise MissingInputError(self.context.__name__, self.context.title)
# TODO: applyChanges isn't reporting "change" correctly (we're
# re-generating the sequence with every edit, and need to be smarter)
@@ -163,10 +176,10 @@
Return ``True`` if there is data and ``False`` otherwise.
"""
- return len(self._generateSequence()) != 0
+ return (self.name + ".count") in self.request.form
def setRenderedValue(self, value):
- """Set the default data for the widget.
+ """Set the data that should be rendered by the widget.
The given value should be used even if the user has entered
data.
@@ -176,51 +189,41 @@
def _generateSequence(self):
"""Take sequence info in the self.request and _data.
+
+ This can only be called if self.hasInput() returns true.
"""
len_prefix = len(self.name)
adding = False
removing = []
- subprefix = re.compile(r'(\d+)\.(.*)$')
- remove_botton_name = 'remove-selected-items-of-seq-' + self.name
if self.context.value_type is None:
+ # Why would this ever happen?
return []
+ # the marker field tells how many individual items were
+ # included in the input; we check for exactly that many input
+ # widgets
+ try:
+ count = int(self.request.form[self.name + ".count"])
+ except ValueError:
+ # could not convert to int; the input was not generated
+ # from the widget as implemented here
+ raise WidgetInputError(self.context.__name__, self.context.title)
# pre-populate
found = {}
- if self._data is not None:
- found = dict(enumerate(self._data))
# now look through the request for interesting values
- for key in self.request.keys():
- if not key.startswith(self.name):
- continue
- token = key[len_prefix+1:] # skip the '.'
- if token == 'add':
- # append a new blank field to the sequence
- adding = True
- elif token.startswith('remove_') and \
- remove_botton_name in self.request:
- # remove the index indicated if we press
- # the "Remove selected items" button.
- # Otherwise we delete the items if we check
- # the box and push the "Change" button.
- removing.append(int(token[7:]))
- else:
- match = subprefix.match(token)
- if match is None:
- continue
- # key refers to a sub field
- i = int(match.group(1))
+ for i in range(count):
+ remove_key = "%s.remove_%d" % (self.name, i)
+ if remove_key in self.request.form:
+ removing.append(i)
+ widget = self._getWidget(i)
+ found[i] = widget.getInputValue()
+ adding = (self.name + ".add") in self.request.form
- # find a widget for the sub-field and use that to parse the
- # request data
- widget = self._getWidget(i)
- value = widget.getInputValue()
- found[i] = value
-
# remove the indicated indexes
- for i in removing:
- del found[i]
+ if (self.name + ".remove") in self.request.form:
+ for i in removing:
+ del found[i]
# generate the list, sorting the dict's contents by key
items = found.items()
@@ -229,6 +232,8 @@
# add an entry to the list if the add button has been pressed
if adding:
+ # Should this be using self.context.value_type.missing_value
+ # instead of None?
sequence.append(None)
return sequence
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-06-01 22:14:18 UTC (rev 30595)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-06-02 03:12:38 UTC (rev 30596)
@@ -28,7 +28,7 @@
from zope.app.form.browser import TextWidget, ObjectWidget
from zope.app.form.browser import TupleSequenceWidget, ListSequenceWidget
from zope.app.form.browser import SequenceWidget
-from zope.app.form.interfaces import IInputWidget
+from zope.app.form.interfaces import IInputWidget, MissingInputError
from zope.app.form import CustomWidgetFactory
from zope.app.form import CustomSequenceWidgetFactory
@@ -81,7 +81,7 @@
self.failIf(self._widget.hasInput())
def test_hasInput(self):
- self._widget.request.form['field.foo.0.bar'] = u'hi, mum'
+ self._widget.request.form['field.foo.count'] = u'0'
self.failUnless(self._widget.hasInput())
def test_customWidgetFactory(self):
@@ -127,14 +127,16 @@
request = TestRequest()
widget = ListSequenceWidget(self.field, TextLine(), request)
self.failIf(widget.hasInput())
- self.assertEquals(widget.getInputValue(), [])
+ self.assertRaises(MissingInputError, widget.getInputValue)
- request = TestRequest(form={'field.foo.add': u'Add bar'})
+ request = TestRequest(form={'field.foo.add': u'Add bar',
+ 'field.foo.count': u'0'})
widget = ListSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertRaises(ValidationError, widget.getInputValue)
- request = TestRequest(form={'field.foo.0.bar': u'Hello world!'})
+ request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
+ 'field.foo.count': u'1'})
widget = ListSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), [u'Hello world!'])
@@ -143,12 +145,13 @@
request = TestRequest()
widget = TupleSequenceWidget(self.field, TextLine(), request)
self.failIf(widget.hasInput())
- self.assertEquals(widget.getInputValue(), ())
+ self.assertRaises(MissingInputError, widget.getInputValue)
check_list = ('input', 'name="field.foo.add"')
self.verifyResult(widget(), check_list)
def test_add(self):
- request = TestRequest(form={'field.foo.add': u'Add bar'})
+ request = TestRequest(form={'field.foo.add': u'Add bar',
+ 'field.foo.count': u'0'})
widget = TupleSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertRaises(ValidationError, widget.getInputValue)
@@ -159,7 +162,8 @@
self.verifyResult(widget(), check_list, inorder=True)
def test_request(self):
- request = TestRequest(form={'field.foo.0.bar': u'Hello world!'})
+ request = TestRequest(form={'field.foo.0.bar': u'Hello world!',
+ 'field.foo.count': u'1'})
widget = TupleSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), (u'Hello world!',))
@@ -168,37 +172,44 @@
request = TestRequest()
widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
- self.assert_(widget.hasInput())
- self.assertEquals(widget.getInputValue(), (u'existing',))
+ self.failIf(widget.hasInput())
+ self.assertRaises(MissingInputError, widget.getInputValue)
check_list = (
'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
'existing',
- 'submit', 'submit', 'field.foo.add'
+ 'submit', 'submit', 'field.foo.add',
+ 'field.foo.count" value="1"',
)
self.verifyResult(widget(), check_list, inorder=True)
widget.setRenderedValue((u'existing', u'second'))
- self.assert_(widget.hasInput())
- self.assertEquals(widget.getInputValue(), (u'existing', u'second'))
+ self.failIf(widget.hasInput())
+ self.assertRaises(MissingInputError, widget.getInputValue)
check_list = (
'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
'existing',
'checkbox', 'field.foo.remove_1', 'input', 'field.foo.1.bar',
'second',
- 'submit', 'submit', 'field.foo.add'
+ 'submit', 'submit', 'field.foo.add',
+ 'field.foo.count" value="2"',
)
self.verifyResult(widget(), check_list, inorder=True)
def test_remove(self):
- request = TestRequest(form={'field.foo.remove_0': u'Hello world!',
+ request = TestRequest(form={
+ 'field.foo.remove_0': u'1',
'field.foo.0.bar': u'existing', 'field.foo.1.bar': u'second',
- 'remove-selected-items-of-seq-field.foo': u'Remove selected items'})
+ 'field.foo.remove': u'Remove selected items',
+ 'field.foo.count': u'2'})
widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing', u'second'))
self.assertEquals(widget.getInputValue(), (u'second',))
check_list = (
'checkbox', 'field.foo.remove_0', 'input', 'field.foo.0.bar',
+ 'existing',
+ 'checkbox', 'field.foo.remove_1', 'input', 'field.foo.1.bar',
'second',
- 'submit', 'submit', 'field.foo.add'
+ 'submit', 'submit', 'field.foo.add',
+ 'field.foo.count" value="2"',
)
self.verifyResult(widget(), check_list, inorder=True)
@@ -207,7 +218,7 @@
self.field.min_length = 2
widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
- self.assertEquals(widget.getInputValue(), (u'existing',))
+ self.assertRaises(MissingInputError, widget.getInputValue)
check_list = (
'input', 'field.foo.0.bar', 'existing',
'input', 'field.foo.1.bar', 'value=""',
@@ -222,7 +233,7 @@
self.field.max_length = 1
widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
- self.assertEquals(widget.getInputValue(), (u'existing',))
+ self.assertRaises(MissingInputError, widget.getInputValue)
s = widget()
self.assertEquals(s.find('field.foo.add'), -1)
More information about the Zope3-Checkins
mailing list