Caution with string values in choice fields in Plone forms with z3c.form

I recently noticed a very strange behaviour while setting initial data for a choice field in a z3c.form. Suppose you have a simple, static vocabulary:

num_voc = SimpleVocabulary([SimpleTerm(value=1, token='1', title='one'),
                            SimpleTerm(value=2, token='2', title='two'),
                            SimpleTerm(value=3, token='3', title='three')])

Here you might skip token, as it is then created by str(<value>) automatically. We will use it in a form by the following schema

num = schema.Choice(title='number', vocabulary=num_voc)

The return value of the forms extractData method will return the value of the dictionary. For example, in a form buttons action method, the selected "two" in the form will result in the integer value 2:

data, errors = self.extractData()
assert data['num'] == 2    # correct

However, if you set the value in the updateWidgets method when the form is opened with a get request, you need to set the widget value to a list containing the token, which happens to be a string:

self.widgets['number'].value = ['2']

The reason is, that the selected attribute of the option in the widget is set by the expression term.token in self.value. While this is natural, as choice fields might support several selected items, it becomes a trap if you use string values. Consider the following example:

title_voc = SimpleVocabulary([SimpleTerm(value='Dr.', title='one'),
                              SimpleTerm(value='Prof.', title='two'),
                              SimpleTerm(value='Prof. Dr.', title='three')])

(Note that the tokens are equal to the values here. For non-ASCII values, value.encode('ascii', 'backslashreplace') is used as of zope.schema >= 4.6.0, i.e. Plone 5.2. But this is a different story and means to need to do the same when setting the value of the widget. Or better, just do not use string values at all.)

If you now miss to set the value as a list ['Prof. Dr.'] but use the string directly, i.e. self.widgets['number'].value = 'Prof. Dr.', you will find all the three options being selected without any error, as term.token in self.value is true for all three cases. Awkward!


No comments.

Add New Comment