[Checkins] SVN: z3c.formwidget.query/trunk/ Implemented multiple
selection.
Malthe Borch
mborch at gmail.com
Thu May 15 20:43:01 EDT 2008
Log message for revision 86778:
Implemented multiple selection.
Changed:
U z3c.formwidget.query/trunk/README.txt
U z3c.formwidget.query/trunk/setup.py
U z3c.formwidget.query/trunk/src/z3c/formwidget/query/README.txt
U z3c.formwidget.query/trunk/src/z3c/formwidget/query/interfaces.py
U z3c.formwidget.query/trunk/src/z3c/formwidget/query/widget.py
-=-
Modified: z3c.formwidget.query/trunk/README.txt
===================================================================
--- z3c.formwidget.query/trunk/README.txt 2008-05-15 15:32:15 UTC (rev 86777)
+++ z3c.formwidget.query/trunk/README.txt 2008-05-16 00:42:59 UTC (rev 86778)
@@ -4,11 +4,16 @@
This package implements a widget that lets users enter a query and
select from the results.
-The widget currently works with ``zope.schema.Choice``-fields
-supplying a query source [1].
+The widget works with ``zope.schema.Choice``-fields supplying a query
+source [1], optionally in conjunction with a collection field which
+then allows multiple selections.
-Results need to implement or adapt to ``ITitledTokenizedTerm``.
+The easiest way to use the widget is to provide one of the following
+as ``widgetFactory``:
+* z3c.formwidget.query.widget.QuerySourceFieldRadioWidget
+* z3c.formwidget.query.widget.QuerySourceFieldCheckboxWidget
+
------
[1] The source needs to implement ``IQuerySource`` as defined in this
Modified: z3c.formwidget.query/trunk/setup.py
===================================================================
--- z3c.formwidget.query/trunk/setup.py 2008-05-15 15:32:15 UTC (rev 86777)
+++ z3c.formwidget.query/trunk/setup.py 2008-05-16 00:42:59 UTC (rev 86778)
@@ -2,7 +2,7 @@
from setuptools import setup, find_packages
setup(name='z3c.formwidget.query',
- version='0.1',
+ version='0.2',
author = "Zope Community",
author_email = "zope3-dev at zope.org",
description = "A source query widget for z3c.form.",
@@ -26,7 +26,7 @@
'zope.component',
'zope.i18nmessageid',
],
- classifiers = ['Development Status :: 4 - Alpha',
+ classifiers = ['Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Framework :: Zope3',
'Intended Audience :: Developers',
Modified: z3c.formwidget.query/trunk/src/z3c/formwidget/query/README.txt
===================================================================
--- z3c.formwidget.query/trunk/src/z3c/formwidget/query/README.txt 2008-05-15 15:32:15 UTC (rev 86777)
+++ z3c.formwidget.query/trunk/src/z3c/formwidget/query/README.txt 2008-05-16 00:42:59 UTC (rev 86778)
@@ -24,6 +24,8 @@
>>> from z3c.formwidget.query.interfaces import IQuerySource
>>> from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+To make things simple, we'll just use unicode strings as values.
+
>>> class ItalianCities(object):
... interface.implements(IQuerySource)
...
@@ -38,6 +40,9 @@
... __contains__ = vocabulary.__contains__
... __iter__ = vocabulary.__iter__
...
+ ... def getTermByValue(self, value):
+ ... return self.vocabulary.by_value[value]
+ ...
... def search(self, query_string):
... return [v for v in self if query_string.lower() in v.value.lower()]
@@ -65,20 +70,29 @@
... description=u'Select a city.',
... source=ItalianCitiesSourceBinder())
-Widget
-------
+Widgets
+-------
- >>> class Content(object):
+There are two widgets available; one that corresponds to a single
+selection or a multi-selection of items from the source.
+
+To enable multiple selections, wrap the ``zope.schema.Choice`` in a field
+derived from ``zope.schema.Collection``.
+
+Let's begin with a single selection.
+
+ >>> class Location(object):
... city = None
- >>> content = Content()
- >>> field = city.bind(content)
+ >>> location = Location()
+ >>> field = city.bind(location)
- >>> from z3c.formwidget.query.widget import QuerySourceFieldWidget
+ >>> from z3c.formwidget.query.widget import QuerySourceFieldRadioWidget
>>> def setupWidget(field, context, request):
- ... widget = QuerySourceFieldWidget(field, request)
+ ... widget = QuerySourceFieldRadioWidget(field, request)
... widget.name = field.__name__
+ ... widget.context = context
... return widget
>>> from z3c.form.testing import TestRequest
@@ -86,17 +100,27 @@
An empty query is not carried out.
- >>> widget = setupWidget(field, content, request)
-
+ >>> widget = setupWidget(field, location, request)
>>> 'type="radio"' in widget()
False
-Let's make a query for "bologna".
+Let's choose a city:
+ >>> location.city = u"Palermo"
+
+ >>> widget = setupWidget(field, location, request)
+
+We now expect a radio button to be present.
+
+ >>> 'type="radio"' in widget()
+ True
+
+We can put a query string in the request, and have our source queried.
+
>>> request = TestRequest(form={
... 'city.widgets.query': u'bologna'})
- >>> widget = setupWidget(field, content, request)
+ >>> widget = setupWidget(field, location, request)
Verify results:
@@ -106,19 +130,101 @@
>>> 'Sorrento' in widget()
False
-We'll select 'Bologna' from the list.
+Selecting 'Bologna' from the list should check the corresponding box.
>>> request = TestRequest(form={
... 'city.widgets.query': u'bologna',
... 'city': ('bologna',)})
- >>> widget = setupWidget(field, content, request)
+ >>> widget = setupWidget(field, location, request)
+ >>> 'checked="checked"' in widget()
+ True
+
+Now we want to try out selection of multiple items.
+
+ >>> cities = zope.schema.Set(
+ ... __name__='cities',
+ ... title=u'Cities',
+ ... description=u'Select one or more cities.',
+ ... value_type=zope.schema.Choice(
+ ... source=ItalianCitiesSourceBinder()
+ ... ))
+
+ >>> class Route(object):
+ ... cities = ()
+
+ >>> route = Route()
+ >>> field = cities.bind(route)
+
+ >>> from z3c.formwidget.query.widget import QuerySourceFieldCheckboxWidget
+
+ >>> def setupWidget(field, context, request):
+ ... widget = QuerySourceFieldCheckboxWidget(field, request)
+ ... widget.name = field.__name__
+ ... widget.context = context
+ ... return widget
+
+ >>> request = TestRequest()
+
+An empty query is not carried out.
+
+ >>> widget = setupWidget(field, route, request)
+ >>> 'type="checkbox"' in widget()
+ False
+
+Let's set a city on the route:
+
+ >>> route.cities = (u"Palermo",)
+
+ >>> widget = setupWidget(field, route, request)
+ >>> 'type="checkbox"' in widget()
+ True
+
+Let's make a query for "bologna".
+
+ >>> request = TestRequest(form={
+ ... 'cities.widgets.query': u'bologna'})
+
+ >>> widget = setupWidget(field, route, request)
+
+Verify results:
+
+ >>> 'Bologna' in widget()
+ True
+
+ >>> 'Sorrento' in widget()
+ False
+
+We'll select 'Bologna' from the list.
+
+ >>> request = TestRequest(form={
+ ... 'cities.widgets.query': u'bologna',
+ ... 'cities': ('bologna',)})
+
+ >>> widget = setupWidget(field, route, request)
+
Verify that Bologna has been selected.
>>> 'checked="checked"' in widget()
True
+Let's try and simulate removing the item we've set on the
+context. We'll submit an empty tuple.
+
+ >>> request = TestRequest(form={
+ ... 'cities': ()})
+
+ >>> widget = setupWidget(field, route, request)
+
+We expect an unchecked box.
+
+ >>> 'type="checkbox"' in widget()
+ True
+
+ >>> 'checked="checked"' in widget()
+ False
+
Todo
----
Modified: z3c.formwidget.query/trunk/src/z3c/formwidget/query/interfaces.py
===================================================================
--- z3c.formwidget.query/trunk/src/z3c/formwidget/query/interfaces.py 2008-05-15 15:32:15 UTC (rev 86777)
+++ z3c.formwidget.query/trunk/src/z3c/formwidget/query/interfaces.py 2008-05-16 00:42:59 UTC (rev 86778)
@@ -1,5 +1,5 @@
-from zope.schema.interfaces import ISource
+from zope.schema.interfaces import ISource, IVocabularyTokenized
-class IQuerySource(ISource):
+class IQuerySource(ISource, IVocabularyTokenized):
def search(query_string):
"""Return values that match query."""
Modified: z3c.formwidget.query/trunk/src/z3c/formwidget/query/widget.py
===================================================================
--- z3c.formwidget.query/trunk/src/z3c/formwidget/query/widget.py 2008-05-15 15:32:15 UTC (rev 86777)
+++ z3c.formwidget.query/trunk/src/z3c/formwidget/query/widget.py 2008-05-16 00:42:59 UTC (rev 86778)
@@ -14,30 +14,22 @@
import z3c.form.field
import z3c.form.widget
import z3c.form.browser.radio
+import z3c.form.browser.checkbox
from z3c.formwidget.query import MessageFactory as _
class QueryTerms(SimpleVocabulary):
zope.interface.implements(ITerms)
- def __init__(self, values):
- terms = [ITitledTokenizedTerm.providedBy(value) and value or \
- ITitledTokenizedTerm(value) for value in values]
-
+ def __init__(self, terms):
super(QueryTerms, self).__init__(terms)
def getTerm(self, value):
- try:
- return self.by_value[value]
- except KeyError:
- raise LookupError(value)
-
+ return self.by_value[value]
+
def getValue(self, token):
- try:
- return self.by_token[token]
- except KeyError:
- raise LookupError(token)
-
+ return self.by_token[token].value
+
class QuerySubForm(z3c.form.form.Form):
zope.interface.implements(z3c.form.interfaces.ISubForm)
@@ -61,10 +53,19 @@
class QueryContext(object):
query = None
-class QuerySourceWidget(z3c.form.browser.radio.RadioWidget):
+class QuerySourceRadioWidget(z3c.form.browser.radio.RadioWidget):
+ """Query source widget that allows single selection."""
+
_queryform = None
_resultsform = None
+ def isChecked(self, term):
+ return term.value in self.selection or term.token in self.value
+
+ @property
+ def source(self):
+ return self.field.source
+
def update(self):
# setup query form
prefix = self.name
@@ -78,7 +79,7 @@
# query source
query = data['query']
- source = self.field.source
+ source = self.source
if IContextSourceBinder.providedBy(source):
source = source(self.context)
@@ -86,28 +87,73 @@
assert ISource.providedBy(source)
if query is not None:
- results = source.search(query)
+ terms = set(source.search(query))
else:
- results = ()
-
+ terms = set()
+
+ # add current selection
+ selection = zope.component.getMultiAdapter(
+ (self.context, self.field), z3c.form.interfaces.IDataManager).get()
+
+ if not isinstance(selection, (tuple, set, list)):
+ selection = (selection,)
+
+ values = [term.value for term in terms]
+
+ map(terms.add,
+ map(source.getTermByValue,
+ filter(lambda value: value and value not in values, selection)))
+
+ self.selection = selection
+
# set terms
- self.terms = QueryTerms(results)
+ self.terms = QueryTerms(terms)
- # update widget
- super(QuerySourceWidget, self).update()
+ # filter on extracted data
+ value = self.extract()
+ if value is not z3c.form.interfaces.NOVALUE:
+ self.selection = map(self.terms.getValue, value)
+
+ # update widget
+ self.updateQueryWidget()
+
+ def updateQueryWidget(self):
+ z3c.form.browser.radio.RadioWidget.update(self)
+
+ def renderQueryWidget(self):
+ return z3c.form.browser.radio.RadioWidget.render(self)
def render(self):
subform = self.subform
if self.terms:
- return "\n".join((subform.render(), super(QuerySourceWidget, self).render()))
+ return "\n".join((subform.render(), self.renderQueryWidget()))
return subform.render()
def __call__(self):
self.update()
return self.render()
+
+class QuerySourceCheckboxWidget(
+ QuerySourceRadioWidget, z3c.form.browser.checkbox.CheckBoxWidget):
+ """Query source widget that allows multiple selections."""
- at zope.component.adapter(zope.schema.interfaces.IChoice, z3c.form.interfaces.IFormLayer)
+ zope.interface.implementsOnly(z3c.form.interfaces.ICheckBoxWidget)
+
+ @property
+ def source(self):
+ return self.field.value_type.source
+
+ def updateQueryWidget(self):
+ z3c.form.browser.checkbox.CheckBoxWidget.update(self)
+
+ def renderQueryWidget(self):
+ return z3c.form.browser.checkbox.CheckBoxWidget.render(self)
+
@zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
-def QuerySourceFieldWidget(field, request):
- return z3c.form.widget.FieldWidget(field, QuerySourceWidget(request))
+def QuerySourceFieldRadioWidget(field, request):
+ return z3c.form.widget.FieldWidget(field, QuerySourceRadioWidget(request))
+
+ at zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
+def QuerySourceFieldCheckboxWidget(field, request):
+ return z3c.form.widget.FieldWidget(field, QuerySourceCheckboxWidget(request))
More information about the Checkins
mailing list