Vocabulary source, function is not iterable, with dexterity in Plone 4.1
I have a custom Dexterity content type with collective.z3c.datagridfield define in the following way:
class ILanguageRow(Interface):
# Interface that defines a datagrid row.
lang = schema.Choice(
title=_(u'Language'), required=True,
开发者_JS百科 source=my_languages,
default=u'en',
)
(...)
This it the function that returns the vocabulary, as in http://plone.org/products/dexterity/documentation/manual/schema-driven-forms/customising-form-behaviour/vocabularies
@grok.provider(IContextSourceBinder)
def languages(context):
"""
Return a vocabulary of language codes and
translated language names.
"""
# z3c.form KSS inline validation hack
if not ISiteRoot.providedBy(context):
for item in getSite().aq_chain:
if ISiteRoot.providedBy(item):
context = item
# retrieve the localized language names.
request = getRequest()
portal_state = getMultiAdapter((context, request), name=u'plone_portal_state')
lang_items = portal_state.locale().displayNames.languages.items()
# build the dictionary
return SimpleVocabulary(
[SimpleTerm(value=lcode, token=lcode, title=lname)\
for lcode, lname in sorted(lang_items) if lcode in config.CV_LANGS]
)
Inside the Edit and Add Form, the Choice field works correctly. But when I attempt to save the content:
TypeError: argument of type 'function' is not iterable
2011-07-08 13:37:40 ERROR Zope.SiteErrorLog 1310125060.840.103138625259 http://localhost:8081/Plone/++add++my.content.types.curriculum
Traceback (innermost last):
Module ZPublisher.Publish, line 126, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 46, in call_object
Module plone.z3cform.layout, line 70, in __call__
Module plone.z3cform.layout, line 54, in update
Module my.content.types.curriculum, line 356, in update
Module plone.z3cform.fieldsets.extensible, line 59, in update
Module plone.z3cform.patch, line 30, in GroupForm_update
Module z3c.form.group, line 134, in update
Module z3c.form.group, line 47, in update
Module z3c.form.group, line 43, in updateWidgets
Module z3c.form.field, line 275, in update
Module z3c.form.browser.multi, line 61, in update
Module z3c.form.browser.widget, line 70, in update
Module z3c.form.widget, line 396, in update
Module z3c.form.widget, line 88, in update
Module z3c.form.widget, line 390, in set
Module collective.z3cform.datagridfield.datagridfield, line 112, in updateWidgets
Module collective.z3cform.datagridfield.datagridfield, line 90, in getWidget
Module z3c.form.browser.widget, line 70, in update
Module z3c.form.object, line 213, in update
Module z3c.form.widget, line 88, in update
Module collective.z3cform.datagridfield.datagridfield, line 216, in set
Module z3c.form.object, line 229, in applyValue
Module z3c.form.validator, line 67, in validate
Module zope.schema._bootstrapfields, line 153, in validate
Module zope.schema._field, line 325, in _validate
TypeError: argument of type 'function' is not iterable
Why is this happening?
This happens when the field hasn't been bound or is missing the context. Normally validation will happen against a "bound" field (bound = field.bind(context)
) so that your context-aware vocabulary can be turned into a static vocabulary for this context. It'll still be a function (not called with the context) when this didn't take place.
I am not familiar enough with the datagrid widget setup to pinpoint where this goes wrong, but it appears that it generates widgets on-the-fly and I suspect it doesn't bind the fields for these correctly. Take a look at DataGridField.getWidget
method of the collective.z3cform.datagridfield.datagridfield
module and try to figure out what's going on there with a debugger and / or file a bug with the authors of the package.
I solved the problem by providing my custom vocabulary as a Named Vocabulary, in this way:
from Products.CMFCore.interfaces import ISiteRoot
from zope.component import getMultiAdapter
from zope.site.hooks import getSite
from zope.globalrequest import getRequest
from my.content import config
class LanguagesVocabulary(object):
grok.implements(IVocabularyFactory)
def __call__(self, context):
# z3c.form KSS inline validation hack
if not ISiteRoot.providedBy(context):
for item in getSite().aq_chain:
if ISiteRoot.providedBy(item):
context = item
# retrieve the localized language names.
request = getRequest()
portal_state = getMultiAdapter((context, request), name=u'plone_portal_state')
lang_items = portal_state.locale().displayNames.languages.items()
# build the dictionary
terms = [SimpleTerm(value=lcode, token=lcode, title=lname)\
for lcode, lname in sorted(lang_items) if lcode in config.CV_LANGS]
return SimpleVocabulary(terms)
grok.global_utility(LanguagesVocabulary, name=u"my.content.LanguagesVocabulary")
and in my Dexterity content type schemata:
class ILanguageRow(Interface):
# Interface that defines a datagrid row.
lang = schema.Choice(
title=_(u'Language'), required=True,
vocabulary=u"my.content.LanguagesVocabulary",
)
This way it works.
精彩评论