[Zope3-checkins] SVN: Zope3/trunk/ Fixed bug #721: Handling of
empty prefixes in zope.formlib and zope.app.form
Jacob Holm
jh at improva.dk
Wed Dec 20 18:34:36 EST 2006
Log message for revision 71638:
Fixed bug #721: Handling of empty prefixes in zope.formlib and zope.app.form
Changed:
U Zope3/trunk/doc/CHANGES.txt
U Zope3/trunk/src/zope/app/form/__init__.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_editview.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_setprefix.py
U Zope3/trunk/src/zope/app/form/browser/widget.py
U Zope3/trunk/src/zope/app/form/tests/test_widget.py
U Zope3/trunk/src/zope/formlib/form.py
U Zope3/trunk/src/zope/formlib/form.txt
-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/doc/CHANGES.txt 2006-12-20 23:34:35 UTC (rev 71638)
@@ -151,7 +151,10 @@
was removed back then already.)
Bug fixes
-
+
+ - Fixed bug #721: Handling of empty prefixes in zope.formlib and
+ zope.app.form
+
- Fixed bug #707: "layer" directive was marked as deprecated in a
confusing way.
Modified: Zope3/trunk/src/zope/app/form/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/form/__init__.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/__init__.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -69,7 +69,7 @@
return self._data is not self._data_marker
def setPrefix(self, prefix):
- if not prefix.endswith("."):
+ if prefix and not prefix.endswith("."):
prefix += '.'
self._prefix = prefix
self.name = prefix + self.context.__name__
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_editview.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_editview.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_editview.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -117,6 +117,14 @@
['test.foo', 'test.bar', 'test.a', 'test.b', 'test.getbaz']
)
+ def test_empty_prefix(self):
+ v = EV(C(), TestRequest())
+ v.setPrefix("")
+ self.assertEqual(
+ [w.name for w in v.widgets()],
+ ['foo', 'bar', 'a', 'b', 'getbaz']
+ )
+
def test_fail_wo_adapter(self):
c = Foo()
request = TestRequest()
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_itemswidget.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -99,6 +99,12 @@
self.assertEqual(widget.name, 'foo.%s' %name)
self.assertEqual(widget.empty_marker_name,
'foo.%s-empty-marker' %name)
+ # Declaring empty prefix
+ widget.setPrefix('')
+ self.assertEqual(widget._prefix, '')
+ self.assertEqual(widget.name, name)
+ self.assertEqual(widget.empty_marker_name,
+ '%s-empty-marker' %name)
def test_convertTokensToValues(self):
widget = self._makeWidget()
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_setprefix.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_setprefix.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_setprefix.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -18,10 +18,11 @@
import unittest
from zope.app.form.browser import TextWidget
+from zope.app.form.browser.tests import support
from zope.publisher.browser import TestRequest
from zope.schema import Text
-class Test(unittest.TestCase):
+class Test(support.VerifyResults, unittest.TestCase):
def setUp(self):
field = Text(__name__ = 'foo')
@@ -38,22 +39,35 @@
check_list = ('type="text"', 'id="spam.foo"', 'name="spam.foo"',
'value="Foo Value 2"', 'size="20"')
self._widget.setRenderedValue(value)
- self._verifyResult(self._widget(), check_list)
+ self.verifyResult(self._widget(), check_list)
check_list = ('type="hidden"',) + check_list[1:-1]
- self._verifyResult(self._widget.hidden(), check_list)
+ self.verifyResult(self._widget.hidden(), check_list)
check_list = ('style="color: red"',) + check_list
self._widget.extra = 'style="color: red"'
- self._verifyResult(self._widget.hidden(), check_list)
+ self.verifyResult(self._widget.hidden(), check_list)
- def _verifyResult(self, result, check_list):
- for check in check_list:
- self.assertNotEqual(-1, result.find(check),
- '"'+check+'" not found in "'+result+'"')
+class TestEmpty(support.VerifyResults, unittest.TestCase):
+ def setUp(self):
+ field = Text(__name__ = 'foo')
+ request = TestRequest()
+ request.form['foo'] = u'Foo Value'
+ self._widget = TextWidget(field, request)
+ self._widget.setPrefix('')
+ def testGetData(self):
+ self.assertEqual(self._widget.getInputValue(), u'Foo Value')
+ def testRender(self):
+ check_list = ('id="foo"', 'name="foo"')
+ self.verifyResult(self._widget(), check_list)
+ self.verifyResult(self._widget.hidden(), check_list)
+
def test_suite():
- return unittest.makeSuite(Test)
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ unittest.makeSuite(TestEmpty),
+ ))
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
Modified: Zope3/trunk/src/zope/app/form/browser/widget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/widget.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/browser/widget.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -175,8 +175,12 @@
>>> widget.error()
''
- You can modify the prefix used to create the widget name as follows:
+ You can use 'setPrefix' to remove or modify the prefix used to create the
+ widget name as follows:
+ >>> widget.setPrefix('')
+ >>> widget.name
+ 'foo'
>>> widget.setPrefix('baz')
>>> widget.name
'baz.foo'
Modified: Zope3/trunk/src/zope/app/form/tests/test_widget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/tests/test_widget.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/app/form/tests/test_widget.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -80,6 +80,12 @@
>>> widget.name
'newprefix.Test'
+ Using the empty string as prefix leaves the prefix off entirely:
+
+ >>> widget.setPrefix('')
+ >>> widget.name
+ 'Test'
+
To configure a widget, call setRenderedValue with a value that the
widget should display:
Modified: Zope3/trunk/src/zope/formlib/form.py
===================================================================
--- Zope3/trunk/src/zope/formlib/form.py 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/formlib/form.py 2006-12-20 23:34:35 UTC (rev 71638)
@@ -50,6 +50,15 @@
_identifier = re.compile('[A-Za-z][a-zA-Z0-9_]*$')
+def expandPrefix(prefix):
+ """Expand prefix string by adding a trailing period if needed.
+
+ expandPrefix(p) should be used instead of p+'.' in most contexts.
+ """
+ if prefix and not prefix.endswith('.'):
+ return prefix + '.'
+ return prefix
+
class FormField:
interface.implements(interfaces.IFormField)
@@ -62,9 +71,7 @@
if name is None:
name = field.__name__
assert name
- if prefix:
- name = prefix + '.' + name
- self.__name__ = name
+ self.__name__ = expandPrefix(prefix) + name
self.prefix = prefix
if interface is None:
interface = field.interface
@@ -170,12 +177,23 @@
interface.implements(interfaces.IWidgets)
- def __init__(self, widgets, prefix_length):
+ def __init__(self, widgets, prefix_length=None, prefix=None):
self.__Widgets_widgets_items__ = widgets
self.__Widgets_widgets_list__ = [w for (i, w) in widgets]
- self.__Widgets_widgets_dict__ = dict(
- [(w.name[prefix_length:], w) for (i, w) in widgets]
- )
+ if prefix is None:
+ # BBB Allow old code using the prefix_length argument.
+ if prefix_length is None:
+ raise TypeError(
+ "One of 'prefix_length' and 'prefix' is required."
+ )
+ self.__Widgets_widgets_dict__ = dict(
+ [(w.name[prefix_length:], w) for (i, w) in widgets]
+ )
+ else:
+ prefix = expandPrefix(prefix)
+ self.__Widgets_widgets_dict__ = dict(
+ [(_widgetKey(w, prefix), w) for (i, w) in widgets]
+ )
def __iter__(self):
return iter(self.__Widgets_widgets_list__)
@@ -261,7 +279,7 @@
prefix = form_prefix
if form_field.prefix:
- prefix += '.' + form_field.prefix
+ prefix = expandPrefix(prefix) + form_field.prefix
widget.setPrefix(prefix)
@@ -278,7 +296,7 @@
widgets.append((not readonly, widget))
- return Widgets(widgets, len(form_prefix)+1)
+ return Widgets(widgets, prefix=form_prefix)
def setUpInputWidgets(form_fields, form_prefix, context, request,
form=None, ignore_request=False):
@@ -289,7 +307,7 @@
prefix = form_prefix
if form_field.prefix:
- prefix += '.' + form_field.prefix
+ prefix = expandPrefix(prefix) + form_field.prefix
widget.setPrefix(prefix)
@@ -301,7 +319,7 @@
widget.setRenderedValue(value)
widgets.append((True, widget))
- return Widgets(widgets, len(form_prefix)+1)
+ return Widgets(widgets, prefix=form_prefix)
def _createWidget(form_field, field, request, iface):
@@ -313,7 +331,7 @@
def getWidgetsData(widgets, form_prefix, data):
errors = []
- form_prefix += '.'
+ form_prefix = expandPrefix(form_prefix)
for input, widget in widgets.__iter_input_and_widget__():
if input and IInputWidget.providedBy(widget):
@@ -380,7 +398,7 @@
prefix = form_prefix
if form_field.prefix:
- prefix += '.' + form_field.prefix
+ prefix = expandPrefix(prefix) + form_field.prefix
widget.setPrefix(prefix)
@@ -391,7 +409,7 @@
widgets.append((not readonly, widget))
- return Widgets(widgets, len(form_prefix)+1)
+ return Widgets(widgets, prefix=form_prefix)
def setUpDataWidgets(form_fields, form_prefix, context, request, data=(),
for_display=False, ignore_request=False):
@@ -407,7 +425,8 @@
prefix = form_prefix
if form_field.prefix:
- prefix += '.' + form_field.prefix
+ prefix = expandPrefix(prefix) + form_field.prefix
+
widget.setPrefix(prefix)
if ((form_field.__name__ in data)
@@ -417,7 +436,7 @@
widgets.append((not readonly, widget))
- return Widgets(widgets, len(form_prefix)+1)
+ return Widgets(widgets, prefix=form_prefix)
class NoInputData(interface.Invalid):
@@ -550,7 +569,7 @@
else:
name = label.encode('hex')
- self.__name__ = prefix + '.' + name
+ self.__name__ = expandPrefix(prefix) + name
if data is None:
data = {}
@@ -562,7 +581,7 @@
result = self.__class__.__new__(self.__class__)
result.__dict__.update(self.__dict__)
result.form = form
- result.__name__ = form.prefix + '.' + result.__name__
+ result.__name__ = expandPrefix(form.prefix) + result.__name__
interface.alsoProvides(result, interfaces.IBoundAction)
return result
@@ -570,13 +589,6 @@
condition = self.condition
return (condition is None) or condition(self.form, self)
- def submitted(self):
- if not self.available():
- return False
- form = self.form
- name = "%s.%s" % (form.prefix, self.__name__)
- return name in form.request.form
-
def validate(self, data):
if self.validator is not None:
return self.validator(self.form, self, data)
Modified: Zope3/trunk/src/zope/formlib/form.txt
===================================================================
--- Zope3/trunk/src/zope/formlib/form.txt 2006-12-20 22:22:35 UTC (rev 71637)
+++ Zope3/trunk/src/zope/formlib/form.txt 2006-12-20 23:34:35 UTC (rev 71638)
@@ -1637,6 +1637,106 @@
# TODO
+
+Omitting the form prefix
+------------------------
+
+For certain use cases (e.g. forms that post data to a different server whose
+software you do not control) it is important to be able to generate forms
+*without* a prefix. Using an empty string for the prefix omits it entirely.
+
+ >>> form_fields = form.Fields(IOrder).select('name')
+ >>> request = TestRequest()
+ >>> widgets = form.setUpWidgets(form_fields, '', None, request)
+ >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE
+ <input class="textType" id="name" name="name" size="20"
+ type="text" value="" />
+
+Of course, getting the widget data still works.
+
+ >>> request.form['name'] = 'foo'
+ >>> widgets = form.setUpWidgets(form_fields, '', None, request)
+ >>> data = {}
+ >>> form.getWidgetsData(widgets, '', data)
+ []
+ >>> data
+ {'name': u'foo'}
+
+And the value from the request is also visible in the rendered form.
+
+ >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE
+ <input class="textType" id="name" name="name" size="20"
+ type="text" value="foo" />
+
+The same is true when using the other setup*Widgets helpers.
+
+ >>> widgets = form.setUpInputWidgets(form_fields, '', None, request)
+ >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE
+ <input class="textType" id="name" name="name" size="20"
+ type="text" value="foo" />
+
+ >>> order = Order(42)
+ >>> widgets = form.setUpEditWidgets(form_fields, '', order, request)
+ >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE
+ <input class="textType" id="name" name="name" size="20"
+ type="text" value="foo" />
+
+ >>> widgets = form.setUpDataWidgets(form_fields, '', None, request)
+ >>> print widgets['name']() # doctest: +NORMALIZE_WHITESPACE
+ <input class="textType" id="name" name="name" size="20"
+ type="text" value="foo" />
+
+Form actions have their own prefix in addition to the form prefix. This can be
+suppressed for each action by passing the empty string as the 'prefix'
+argument.
+
+ >>> class MyForm(form.Form):
+ ...
+ ... prefix = ''
+ ... form_fields = form.Fields()
+ ...
+ ... @form.action('Button 1', name='button1')
+ ... def handle_button1(self, action, data):
+ ... self.status = 'Button 1 detected'
+ ...
+ ... @form.action('Button 2', prefix='', name='button2')
+ ... def handle_button2(self, action, data):
+ ... self.status = 'Button 2 detected'
+ ...
+ >>> request = TestRequest()
+ >>> request.form['actions.button1'] = ''
+ >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE
+ Button 1 detected
+ <input type="submit" id="actions.button1" name="actions.button1"
+ value="Button 1" class="button" />
+ <input type="submit" id="button2" name="button2"
+ value="Button 2" class="button" />
+ >>> request = TestRequest()
+ >>> request.form['button2'] = ''
+ >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE
+ Button 2 detected
+ <input type="submit" id="actions.button1" name="actions.button1"
+ value="Button 1" class="button" />
+ <input type="submit" id="button2" name="button2"
+ value="Button 2" class="button" />
+
+It is also possible to keep the form prefix and just suppress the 'actions' prefix.
+
+ >>> class MyForm(form.Form):
+ ...
+ ... form_fields = form.Fields()
+ ...
+ ... @form.action('Button', prefix='', name='button')
+ ... def handle_button(self, action, data):
+ ... self.status = 'Button detected'
+ ...
+ >>> request = TestRequest()
+ >>> request.form['form.button'] = ''
+ >>> print MyForm(None, request)() # doctest: +NORMALIZE_WHITESPACE
+ Button detected
+ <input type="submit" id="form.button" name="form.button"
+ value="Button" class="button" />
+
Additional Cases
================
More information about the Zope3-Checkins
mailing list