I have a z3c.form that's used during registration. One of the fields is a list of emails users may wish to sign up for.
from zope import schema
from zope.schema.vocabulary import SimpleVocabulary
emailVocab = SimpleVocabulary.fromItems((
('sysn', u'System notifications (strongly recommended)'),
('mark', u'Marketing emails'),
('offe', u'Special offers')))
...
email_optin = schema.List(
title = u'',
description = u'',
required = False,
value_type = schema.Choice(source=emailVocab))
I'd like to have the first of these to be selected by default, while the others should not be. I can't see a way in the spec to do this. Any ideas?
Simplest case is as documented in Modelling using zope.schema, default value section, which z3c.form
picks up (relevant documentation). However, this is complicated by the fact that the default values should not be mutable as the instance is shared across everything, so for safety sake a defaultFactory
argument is implemented for handling this. Putting all that together, you should have something like this:
import zope.schema
import zope.interface
from zope.schema.vocabulary import SimpleVocabulary
emailVocab = SimpleVocabulary.fromItems((
('sysn', u'System notifications (strongly recommended)'),
('mark', u'Marketing emails'),
('offe', u'Special offers')))
def default_email():
return [u'Special offers'] # example
class IEmailPreference(zope.interface.Interface):
# ...
email_optin = zope.schema.List(
title=u'',
description=u'',
required=False,
value_type=zope.schema.Choice(source=emailVocab),
defaultFactory=default_email,
)
Do note that the actual value used for the default is not the token
part but the value
part, hence the string 'Special offers'
is returned instead of 'offe'
. It is documented in the documentation about Vocabularies. If the human readable part is intended to be the title and you wish the actual value to be the same as the token you will need to adjust your code accordingly. Otherwise, to select the first one you simply have default_email
return [u'System notifications (strongly recommended)']
.
For completeness, your form module might look something like this:
from z3c.form.browser.checkbox import CheckBoxFieldWidget
from z3c.form.form import Form
class EmailPreferenceForm(Form):
fields = z3c.form.field.Fields(IEmailPreference)
fields['email_optin'].widgetFactory = CheckBoxFieldWidget
Alternatively, you can use value discriminators to approach this issue if you don't wish to populate the interface with a default value or factory for that, but this is a lot more effort to set up so I generally avoid dealing with that when this is good enough.