Skip to content

Commit

Permalink
Look up widgets for fields implementing IChoice by querying for an ad…
Browse files Browse the repository at this point in the history
…apter for (field, field.vocabulary, request) instead of just (field, request), so it can be differentiated according to the type of the source used for the field. (Inspired by how zope.formlib handles the same case.)

--This line, and those below, will
  be ignored--

M    CHANGES.txt
M    src/z3c/form/browser/select.zcml
M    src/z3c/form/browser/tests.py
M    src/z3c/form/browser/select.py
A    src/z3c/form/browser/select-source.txt
M    src/z3c/form/testing.py
  • Loading branch information
wosc committed Feb 12, 2009
1 parent 07c55ba commit fb2e612
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Expand Up @@ -107,6 +107,9 @@ Version 2.0.0 (unreleased)
- Bug: SingleCheckBoxFieldWidget doesn't repeat the label twice (once in
<div class="label">, and once in the <label> next to the checkbox).

- Feature: The widget for fields implementing IChoice is now looked up by
querying for an adapter for (field, field.vocabulary, request) so it can be
differentiated according to the type of the source used for the field.

Version 1.9.0 (2008-08-26)
--------------------------
Expand Down
72 changes: 72 additions & 0 deletions src/z3c/form/browser/select-source.txt
@@ -0,0 +1,72 @@
=====================================
Customizing widget lookup for IChoice
=====================================

Widgets for fields implementing IChoice are looked up not only according to the
field, but also according to the source used by the field.

>>> import z3c.form.testing
>>> import zope.interface
>>> import zope.component
>>> from z3c.form import interfaces
>>> from z3c.form.testing import TestRequest

>>> z3c.form.testing.setupFormDefaults()
>>> def setupWidget(field):
... request = TestRequest()
... widget = zope.component.getMultiAdapter((field, request),
... interfaces.IFieldWidget)
... widget.id = 'foo'
... widget.name = 'bar'
... return widget

We define a sample field and source:

>>> from zope.schema import vocabulary
>>> terms = [vocabulary.SimpleTerm(*value) for value in
... [(True, 'yes', 'Yes'), (False, 'no', 'No')]]
>>> vocabulary = vocabulary.SimpleVocabulary(terms)
>>> field = zope.schema.Choice(default=True, vocabulary=vocabulary)

The default widget is the SelectWidget:

>>> widget = setupWidget(field)
>>> type(widget)
<class 'z3c.form.browser.select.SelectWidget'>

But now we define a marker interface and have our source provide it:

>>> from z3c.form.widget import FieldWidget
>>> class ISampleSource(zope.interface.Interface):
... pass
>>> zope.interface.alsoProvides(vocabulary, ISampleSource)

We can then create and register a special widget for fields using sources with
the ISampleSource marker:

>>> class SampleSelectWidget(z3c.form.browser.select.SelectWidget):
... pass
>>> def SampleSelectFieldWidget(field, source, request):
... return FieldWidget(field, SampleSelectWidget(request))
>>> zope.component.provideAdapter(
... SampleSelectFieldWidget,
... (zope.schema.interfaces.IChoice, ISampleSource, interfaces.IFormLayer),
... interfaces.IFieldWidget)

If we now look up the widget for the field, we get the specialized widget:

>>> widget = setupWidget(field)
>>> type(widget)
<class 'SampleSelectWidget'>

Backwards compatibility
-----------------------

To maintain backwards compatibility, SelectFieldWidget() still can be called
without passing a source:

>>> import z3c.form.browser.select
>>> request = TestRequest()
>>> widget = z3c.form.browser.select.SelectFieldWidget(field, request)
>>> type(widget)
<class 'z3c.form.browser.select.SelectWidget'>
22 changes: 19 additions & 3 deletions src/z3c/form/browser/select.py
Expand Up @@ -75,9 +75,25 @@ def update(self):

@zope.component.adapter(zope.schema.interfaces.IChoice, interfaces.IFormLayer)
@zope.interface.implementer(interfaces.IFieldWidget)
def SelectFieldWidget(field, request):
def ChoiceWidgetDispatcher(field, request):
"""Dispatch widget for IChoice based also on its source."""
return zope.component.getMultiAdapter((field, field.vocabulary, request),
interfaces.IFieldWidget)


# IBaseVocabulary can change to ISource once vocabularies are deprecated
@zope.component.adapter(zope.schema.interfaces.IChoice,
zope.schema.interfaces.IBaseVocabulary,
interfaces.IFormLayer)
@zope.interface.implementer(interfaces.IFieldWidget)
def SelectFieldWidget(field, source, request=None):
"""IFieldWidget factory for SelectWidget."""
return FieldWidget(field, SelectWidget(request))
# BBB: emulate our pre-2.0 signature (field, request)
if request is None:
real_request = source
else:
real_request = request
return FieldWidget(field, SelectWidget(real_request))


@zope.component.adapter(
Expand All @@ -98,4 +114,4 @@ def CollectionSelectFieldWidget(field, request):
@zope.interface.implementer(interfaces.IFieldWidget)
def CollectionChoiceSelectFieldWidget(field, value_type, request):
"""IFieldWidget factory for SelectWidget."""
return SelectFieldWidget(field, request)
return SelectFieldWidget(field, None, request)
4 changes: 4 additions & 0 deletions src/z3c/form/browser/select.zcml
Expand Up @@ -10,6 +10,10 @@
/>
</class>

<adapter
factory=".select.ChoiceWidgetDispatcher"
/>

<adapter
factory=".select.SelectFieldWidget"
/>
Expand Down
5 changes: 5 additions & 0 deletions src/z3c/form/browser/tests.py
Expand Up @@ -71,6 +71,11 @@ def test_suite():
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
checker=checker,
),
DocFileSuite('select-source.txt',
setUp=setUp, tearDown=testing.tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
checker=checker,
),
DocFileSuite('submit.txt',
setUp=setUp, tearDown=testing.tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
Expand Down
1 change: 1 addition & 0 deletions src/z3c/form/testing.py
Expand Up @@ -281,6 +281,7 @@ def setupFormDefaults():
IPageTemplate, name=interfaces.DISPLAY_MODE)

# Select Widget
zope.component.provideAdapter(select.ChoiceWidgetDispatcher)
zope.component.provideAdapter(select.SelectFieldWidget)
zope.component.provideAdapter(
widget.WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'),
Expand Down

0 comments on commit fb2e612

Please sign in to comment.