[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/form/ Fix
SequenceWidgets
Roger Ineichen
roger at projekt01.ch
Sun Feb 20 10:36:06 EST 2005
Log message for revision 29226:
Fix SequenceWidgets
Added a CustomSequenceWidgetFactory for using object widgets within a sequence
Added a howto for ObjectWidget
Changed:
U Zope3/trunk/src/zope/app/form/__init__.py
A Zope3/trunk/src/zope/app/form/browser/objectwidget.txt
U Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_objectwidget.py
U Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
U Zope3/trunk/src/zope/app/form/tests/test_widget.py
-=-
Modified: Zope3/trunk/src/zope/app/form/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/form/__init__.py 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/__init__.py 2005-02-20 15:36:05 UTC (rev 29226)
@@ -101,3 +101,19 @@
for name, value in self.kw.items():
setattr(instance, name, value)
return instance
+
+class CustomSequenceWidgetFactory(object):
+ """Custom Widget Factory."""
+ implements(IViewFactory)
+
+ def __init__(self, widget_factory, *args, **kw):
+ self._widget_factory = widget_factory
+ self.args = args
+ self.kw = kw
+
+ def __call__(self, context, field, request):
+ args = (context, field, request) + self.args
+ instance = self._widget_factory(*args)
+ for name, value in self.kw.items():
+ setattr(instance, name, value)
+ return instance
Added: Zope3/trunk/src/zope/app/form/browser/objectwidget.txt
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/objectwidget.txt 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/browser/objectwidget.txt 2005-02-20 15:36:05 UTC (rev 29226)
@@ -0,0 +1,149 @@
+=============
+Object Widget
+=============
+
+The following example shows a Family with Mother and Father.
+First define the interface for a person:
+
+ >>> from zope.interface import Interface, implements
+ >>> from zope.schema import TextLine
+
+ >>> class IPerson(Interface):
+ ... """Interface for Persons."""
+ ...
+ ... name = TextLine(title=u'Name', description=u'The first name')
+
+Let's define the class:
+
+ >>> class Person(object):
+ ...
+ ... implements(IPerson)
+ ...
+ ... def __init__(self, name=''):
+ ... self.name = name
+
+Let's define the interface family:
+
+ >>> from zope.schema import Object
+
+ >>> class IFamily(Interface):
+ ... """The familiy interface."""
+ ...
+ ... mother = Object(title=u'Mother',
+ ... required=False,
+ ... schema=IPerson)
+ ...
+ ... father = Object(title=u'Father',
+ ... required=False,
+ ... schema=IPerson)
+
+Let's define the class familiy with FieldProperty's mother and father
+FieldProperty validate the values if they get added:
+
+ >>> from zope.schema.fieldproperty import FieldProperty
+
+ >>> class Family(object):
+ ... """The familiy interface."""
+ ...
+ ... implements(IFamily)
+ ...
+ ... mother = FieldProperty(IFamily['mother'])
+ ... father = FieldProperty(IFamily['father'])
+ ...
+ ... def __init__(self, mother=None, father=None):
+ ... self.mother = mother
+ ... self.father = father
+
+Let's make a instance of Family with None attributes:
+
+ >>> family = Family()
+ >>> bool(family.mother == None)
+ True
+
+ >>> bool(family.father == None)
+ True
+
+Let's make a instance of Family with None attributes:
+
+ >>> mother = Person(u'Margrith')
+ >>> father = Person(u'Joe')
+ >>> family = Family(mother, father)
+ >>> IPerson.providedBy(family.mother)
+ True
+
+ >>> IPerson.providedBy(family.father)
+ True
+
+Let's define a dummy class which doesn't implements IPerson:
+
+ >>> class Dummy(object):
+ ... """Dummy class."""
+ ... def __init__(self, name=''):
+ ... self.name = name
+
+Raise a SchemaNotProvided exception if we add a Dummy instance to a Family
+object:
+
+ >>> foo = Dummy('foo')
+ >>> bar = Dummy('bar')
+ >>> family = Family(foo, bar)
+ Traceback (most recent call last):
+ ...
+ SchemaNotProvided
+
+Now let's setup a enviroment for use the widget like in a real application:
+
+ >>> from zope.app.testing import ztapi
+ >>> from zope.publisher.browser import TestRequest
+ >>> from zope.schema.interfaces import ITextLine
+ >>> from zope.schema import TextLine
+ >>> from zope.app.form.browser import TextWidget
+ >>> from zope.app.form.browser import ObjectWidget
+ >>> from zope.app.form.interfaces import IInputWidget
+
+Register the TextLine widget used in the IPerson interface for the field 'name'.
+
+ >>> ztapi.browserViewProviding(ITextLine, TextWidget, IInputWidget)
+
+Let's define a request and provide input value for the mothers name used
+in the family object:
+
+ >>> request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
+ >>> request.form['field.mother.name'] = u'Margrith Ineichen'
+
+Before we update the object let's check the value name of the mother
+instance on the family object:
+
+ >>> family.mother.name
+ u'Margrith'
+
+Now let's initialize a ObjectWidget with the right attributes:
+
+ >>> mother_field = IFamily['mother']
+ >>> factory = Person
+ >>> widget = ObjectWidget(mother_field, request, factory)
+
+Now comes the magic. Apply changes means we force the ObjectWidget to read
+the request, extract the value and save it on the content. The ObjectWidget
+instance uses a real Person class (factory) for add the value. The value is
+temporary stored in this factory class. The ObjectWidget reads the value from
+this factory and set it to the attribute 'name' of the instance mother
+(The object mother is allready there). If we don't have a instance mother
+allready store in the family object, the factory instance will be stored
+directly to the family attribute mother. For more information see the method
+'applyChanges()' in the interface
+zope.app.form.browser.objectwidget.ObjectWidget.
+
+ >>> widget.applyChanges(family)
+ True
+
+Test the updated mother's name value on the object family:
+
+ >>> family.mother.name
+ u'Margrith Ineichen'
+
+ >>> IPerson.providedBy(family.mother)
+ True
+
+So, now you know my mothers and fathers name. I hope it's also clear how to
+use the Object field and the ObjectWidget.
Property changes on: Zope3/trunk/src/zope/app/form/browser/objectwidget.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: Zope3/trunk/src/zope/app/form/browser/sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/browser/sequencewidget.py 2005-02-20 15:36:05 UTC (rev 29226)
@@ -39,7 +39,7 @@
_type = tuple
_data = () # pre-existing sequence items (from setRenderedValue)
- def __init__(self, context, request, subwidget=None):
+ def __init__(self, context, field, request, subwidget=None):
super(SequenceWidget, self).__init__(context, request)
self.subwidget = subwidget
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_objectwidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_objectwidget.py 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_objectwidget.py 2005-02-20 15:36:05 UTC (rev 29226)
@@ -15,7 +15,8 @@
$Id$
"""
-import unittest, doctest
+import unittest
+from zope.testing import doctest
from zope.app.testing import ztapi
from zope.interface import Interface, implements
@@ -100,6 +101,7 @@
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(ObjectWidgetTest),
+ doctest.DocFileSuite('../objectwidget.txt'),
doctest.DocTestSuite(),
))
Modified: Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/browser/tests/test_sequencewidget.py 2005-02-20 15:36:05 UTC (rev 29226)
@@ -30,6 +30,7 @@
from zope.app.form.browser import SequenceWidget
from zope.app.form.interfaces import IInputWidget
from zope.app.form import CustomWidgetFactory
+from zope.app.form import CustomSequenceWidgetFactory
from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
@@ -57,7 +58,7 @@
field = field.bind(self.content)
request = TestRequest(HTTP_ACCEPT_LANGUAGE='pl')
request.form['field.foo'] = u'Foo Value'
- self._widget = self._WidgetFactory(field, request)
+ self._widget = self._WidgetFactory(field, TextLine(), request)
def _FieldFactory(self, **kw):
kw.update({
@@ -93,16 +94,16 @@
request = TestRequest()
# set up the custom widget factory and verify that it works
- sw = CustomWidgetFactory(ListSequenceWidget)
- widget = sw(self.field, request)
+ sw = CustomSequenceWidgetFactory(ListSequenceWidget)
+ widget = sw(self.field, TextLine(), request)
assert widget.subwidget is None
assert widget.context.value_type is value_type
# set up a variant that specifies the subwidget to use and verify it
class PollOption(object) : pass
ow = CustomWidgetFactory(ObjectWidget, PollOption)
- sw = CustomWidgetFactory(ListSequenceWidget, subwidget=ow)
- widget = sw(self.field, request)
+ sw = CustomSequenceWidgetFactory(ListSequenceWidget, subwidget=ow)
+ widget = sw(self.field, TextLine(), request)
assert widget.subwidget is ow
assert widget.context.value_type is value_type
@@ -116,7 +117,7 @@
class PollOption(object) : pass
ow = CustomWidgetFactory(ObjectWidget, PollOption)
- widget = SequenceWidget(self.field, request, subwidget=ow)
+ widget = SequenceWidget(self.field, TextLine(), request, subwidget=ow)
assert widget.subwidget is ow
def test_list(self):
@@ -124,23 +125,23 @@
__name__=u'foo',
value_type=TextLine(__name__=u'bar'))
request = TestRequest()
- widget = ListSequenceWidget(self.field, request)
+ widget = ListSequenceWidget(self.field, TextLine(), request)
self.failIf(widget.hasInput())
self.assertEquals(widget.getInputValue(), [])
request = TestRequest(form={'field.foo.add': u'Add bar'})
- widget = ListSequenceWidget(self.field, request)
+ 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!'})
- widget = ListSequenceWidget(self.field, request)
+ widget = ListSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), [u'Hello world!'])
def test_new(self):
request = TestRequest()
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
self.failIf(widget.hasInput())
self.assertEquals(widget.getInputValue(), ())
check_list = ('input', 'name="field.foo.add"')
@@ -148,7 +149,7 @@
def test_add(self):
request = TestRequest(form={'field.foo.add': u'Add bar'})
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertRaises(ValidationError, widget.getInputValue)
check_list = (
@@ -159,13 +160,13 @@
def test_request(self):
request = TestRequest(form={'field.foo.0.bar': u'Hello world!'})
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), (u'Hello world!',))
def test_existing(self):
request = TestRequest()
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
self.assert_(widget.hasInput())
self.assertEquals(widget.getInputValue(), (u'existing',))
@@ -191,7 +192,7 @@
request = TestRequest(form={'field.foo.remove_0': u'Hello world!',
'field.foo.0.bar': u'existing', 'field.foo.1.bar': u'second',
'remove-selected-items-of-seq-field.foo': u'Remove selected items'})
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing', u'second'))
self.assertEquals(widget.getInputValue(), (u'second',))
check_list = (
@@ -204,7 +205,7 @@
def test_min(self):
request = TestRequest()
self.field.min_length = 2
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
self.assertEquals(widget.getInputValue(), (u'existing',))
check_list = (
@@ -219,7 +220,7 @@
def test_max(self):
request = TestRequest()
self.field.max_length = 1
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
self.assertEquals(widget.getInputValue(), (u'existing',))
s = widget()
@@ -228,7 +229,7 @@
def test_anonymousfield(self):
self.field = Tuple(__name__=u'foo', value_type=TextLine())
request = TestRequest()
- widget = TupleSequenceWidget(self.field, request)
+ widget = TupleSequenceWidget(self.field, TextLine(), request)
widget.setRenderedValue((u'existing',))
s = widget()
check_list = (
Modified: Zope3/trunk/src/zope/app/form/tests/test_widget.py
===================================================================
--- Zope3/trunk/src/zope/app/form/tests/test_widget.py 2005-02-20 15:21:58 UTC (rev 29225)
+++ Zope3/trunk/src/zope/app/form/tests/test_widget.py 2005-02-20 15:36:05 UTC (rev 29226)
@@ -23,7 +23,8 @@
from zope.publisher.browser import TestRequest
from zope.schema import Text
-from zope.app.form import Widget, CustomWidgetFactory
+from zope.app.form import Widget
+from zope.app.form import CustomWidgetFactory, CustomSequenceWidgetFactory
from zope.app.form.interfaces import IWidget
from zope.app.testing.placelesssetup import setUp, tearDown
@@ -159,6 +160,31 @@
'baz'
"""
+class TestCustomSequenceWidgetFactory(object):
+ """Tests the custom sequence widget factory.
+
+ Custom widgets can be created using a custom widget factory. Factories
+ are used to assign attribute values to widgets they create. The custom
+ sequence widget factory can be used for a list or tuple of objects:
+
+ >>> from zope.schema import TextLine, List
+ >>> from zope.app.form.browser import ListSequenceWidget
+ >>> value_type = TextLine(__name__=u'bar')
+ >>> field = List( __name__=u'foo', value_type=value_type )
+
+ >>> ow = CustomWidgetFactory(FooWidget, bar='baz')
+ >>> sw = CustomSequenceWidgetFactory(ListSequenceWidget, subwidget=ow)
+ >>> widget = sw(field, TextLine(), request)
+ >>> isinstance(widget, ListSequenceWidget)
+ True
+ >>> isinstance(widget.subwidget, CustomWidgetFactory)
+ True
+ >>> widget.subwidget is ow
+ True
+ >>> widget.context.value_type is value_type
+ True
+ """
+
def test_suite():
return TestSuite((
DocTestSuite(setUp=setUp, tearDown=tearDown),
More information about the Zope3-Checkins
mailing list