Skip to content

Commit

Permalink
Merge r29226:
Browse files Browse the repository at this point in the history
  Fix SequenceWidgets
  Added a CustomSequenceWidgetFactory for using object widgets within a sequence
  Added a howto for ObjectWidget
  • Loading branch information
philikon committed May 30, 2005
2 parents d09788a + 5d3fa8e commit c7087c0
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 21 deletions.
16 changes: 16 additions & 0 deletions __init__.py
Expand Up @@ -93,3 +93,19 @@ def __call__(self, context, request):
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
149 changes: 149 additions & 0 deletions browser/objectwidget.txt
@@ -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.tests 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.
2 changes: 1 addition & 1 deletion browser/sequencewidget.py
Expand Up @@ -39,7 +39,7 @@ class SequenceWidget(BrowserWidget, InputWidget):
_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
Expand Down
7 changes: 5 additions & 2 deletions browser/tests/test_objectwidget.py
Expand Up @@ -15,8 +15,8 @@
$Id$
"""

import unittest, doctest
import unittest
from zope.testing import doctest

from zope.app.tests import ztapi
from zope.interface import Interface, implements
Expand All @@ -27,6 +27,7 @@
from zope.app.form.browser import TextWidget, ObjectWidget
from zope.interface.verify import verifyClass
from zope.app.form.browser.tests.test_browserwidget import BrowserWidgetTest
from zope.app.tests.placelesssetup import setUp, tearDown

class ITestContact(Interface):
name = TextLine()
Expand Down Expand Up @@ -101,6 +102,8 @@ def test_applyChangesNoChange(self):
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(ObjectWidgetTest),
doctest.DocFileSuite('../objectwidget.txt',
setUp=setUp, tearDown=tearDown),
doctest.DocTestSuite(),
))

Expand Down
35 changes: 18 additions & 17 deletions browser/tests/test_sequencewidget.py
Expand Up @@ -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

Expand Down Expand Up @@ -57,7 +58,7 @@ class TestObject(object):
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({
Expand Down Expand Up @@ -93,16 +94,16 @@ def test_customWidgetFactory(self):
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

Expand All @@ -116,39 +117,39 @@ def test_subwidget(self):

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):
self.field = List(
__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"')
self.verifyResult(widget(), check_list)

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 = (
Expand All @@ -159,13 +160,13 @@ def test_add(self):

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',))
Expand All @@ -191,7 +192,7 @@ def test_remove(self):
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 = (
Expand All @@ -204,7 +205,7 @@ def test_remove(self):
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 = (
Expand All @@ -219,7 +220,7 @@ def test_min(self):
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()
Expand All @@ -228,7 +229,7 @@ def test_max(self):
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 = (
Expand Down
28 changes: 27 additions & 1 deletion tests/test_widget.py
Expand Up @@ -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.tests.placelesssetup import setUp, tearDown

Expand Down Expand Up @@ -159,6 +160,31 @@ class TestCustomWidgetFactory(object):
'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),
Expand Down

0 comments on commit c7087c0

Please sign in to comment.