[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