============================ Select Widget, missing terms ============================ The select widget allows you to select one or more values from a set of given options. The "SELECT" and "OPTION" elements are described here: http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#edef-SELECT As for all widgets, the select widget must provide the new ``IWidget`` interface: >>> from z3c.form import interfaces >>> from z3c.form.browser import select The widget can be instantiated only using the request: >>> from z3c.form.testing import TestRequest >>> request = TestRequest() >>> widget = select.SelectWidget(request) Before rendering the widget, one has to set the name and id of the widget: >>> widget.id = 'widget-id' >>> widget.name = 'widget.name' We also need to register the template for at least the widget and request: >>> import zope.component >>> from zope.pagetemplate.interfaces import IPageTemplate >>> from z3c.form.testing import getPath >>> from z3c.form.widget import WidgetTemplateFactory >>> zope.component.provideAdapter( ... WidgetTemplateFactory(getPath('select_input.pt'), 'text/html'), ... (None, None, None, None, interfaces.ISelectWidget), ... IPageTemplate, name=interfaces.INPUT_MODE) We need some context: >>> class IPerson(zope.interface.Interface): ... rating = zope.schema.Choice( ... vocabulary='Ratings') >>> @zope.interface.implementer(IPerson) ... class Person(object): ... pass >>> person = Person() Let's provide some values for this widget. We can do this by defining a source providing ``ITerms``. This source uses descriminators which will fit our setup. >>> import zope.schema.interfaces >>> from zope.schema.vocabulary import SimpleVocabulary >>> from zope.schema.vocabulary import SimpleTerm >>> import z3c.form.term >>> from zope.schema import vocabulary >>> ratings = vocabulary.SimpleVocabulary([ ... vocabulary.SimpleVocabulary.createTerm(0, '0', u'bad'), ... vocabulary.SimpleVocabulary.createTerm(1, '1', u'okay'), ... vocabulary.SimpleVocabulary.createTerm(2, '2', u'good') ... ]) >>> def RatingsVocabulary(obj): ... return ratings >>> vr = vocabulary.getVocabularyRegistry() >>> vr.register('Ratings', RatingsVocabulary) >>> class SelectionTerms(z3c.form.term.MissingChoiceTermsVocabulary): ... def __init__(self, context, request, form, field, widget): ... self.context = context ... self.field = field ... self.terms = ratings ... self.widget = widget ... ... def _makeMissingTerm(self, token): ... if token == 'x': ... return super(SelectionTerms, self)._makeMissingTerm(token) ... else: ... raise LookupError >>> zope.component.provideAdapter(SelectionTerms, ... (None, interfaces.IFormLayer, None, None, interfaces.ISelectWidget) ) >>> import z3c.form.datamanager >>> zope.component.provideAdapter(z3c.form.datamanager.AttributeField) Now let's try if we get widget values: >>> widget.update() >>> print(widget.render()) If we set the widget value to "x", then it should be present and selected: >>> widget.value = ['x'] >>> widget.context = person >>> widget.field = IPerson['rating'] >>> zope.interface.alsoProvides(widget, interfaces.IContextAware) >>> person.rating = 'x' >>> widget.terms = None >>> widget.update() >>> print(widget.render()) If we set the widget value to "y", then it should NOT be around: >>> widget.value = ['y'] >>> widget.update() >>> print(widget.render()) Let's now make sure that we can extract user entered data from a widget: >>> widget.request = TestRequest(form={'widget.name': ['c']}) >>> widget.update() >>> widget.extract() Well, only of it matches the context's current value: >>> widget.request = TestRequest(form={'widget.name': ['x']}) >>> widget.update() >>> widget.extract() ['x'] When "No value" is selected, then no verification against the terms is done: >>> widget.request = TestRequest(form={'widget.name': ['--NOVALUE--']}) >>> widget.update() >>> widget.extract(default=1) ['--NOVALUE--'] Let's now make sure that we can extract user entered missing data from a widget: >>> widget.request = TestRequest(form={'widget.name': ['x']}) >>> widget.update() >>> widget.extract() ['x'] >>> widget.request = TestRequest(form={'widget.name': ['y']}) >>> widget.update() >>> widget.extract() Unfortunately, when nothing is selected, we do not get an empty list sent into the request, but simply no entry at all. For this we have the empty marker, so that: >>> widget.request = TestRequest(form={'widget.name-empty-marker': '1'}) >>> widget.update() >>> widget.extract() [] If nothing is found in the request, the default is returned: >>> widget.request = TestRequest() >>> widget.update() >>> widget.extract(default=1) 1 Let's now make sure that a bogus value causes extract to return the default as described by the interface: >>> widget.request = TestRequest(form={'widget.name': ['y']}) >>> widget.update() >>> widget.extract(default=1) 1 Display Widget -------------- The select widget comes with a template for ``DISPLAY_MODE``. Let's register it first: >>> zope.component.provideAdapter( ... WidgetTemplateFactory(getPath('select_display.pt'), 'text/html'), ... (None, None, None, None, interfaces.ISelectWidget), ... IPageTemplate, name=interfaces.DISPLAY_MODE) Let's see what happens if we have values that are not in the vocabulary: >>> widget.required = True >>> widget.mode = interfaces.DISPLAY_MODE >>> widget.value = ['0', '1', 'x'] >>> widget.update() >>> print(widget.render()) bad, okay, Missing: x Hidden Widget ------------- The select widget comes with a template for ``HIDDEN_MODE``. Let's register it first: >>> zope.component.provideAdapter( ... WidgetTemplateFactory(getPath('select_hidden.pt'), 'text/html'), ... (None, None, None, None, interfaces.ISelectWidget), ... IPageTemplate, name=interfaces.HIDDEN_MODE) Let's see what happens if we have values that are not in the vocabulary: >>> widget.mode = interfaces.HIDDEN_MODE >>> widget.value = ['0', 'x'] >>> widget.update() >>> print(widget.render())