OK, here's how I did it:
def variabledecode(func):
def newfunc(self, *args, **kwargs):
from formencode.variabledecode import variable_decode
return func(self, *args, **(variable_decode(kwargs)))
newfunc.func_name = func.func_name
return newfunc
...
@turbogears.expose(
html="newsandevents.templates.admin_article_add",
validators=dict(
...
),
)
@variabledecode
def add(self, action=None, **kwargs):
...
and the template:
<div id="audience" py:for="audience in audiences">
<input type="checkbox" name="audience-${audience.id}"
py:attrs="value=audience.id"
py:content="audience.description">Audience</input></div>
The problem with this is code is, I have to have this sequence:
@turbogears.expose
@variabledecode
def func(...):
Otherwise, it won't be exposed, since the exposed method is wrapped by
a non-exposed function. I'll probably just change variabledecode() to
expose itself if the wrapped function is exposed. It sounds perverted
so it must be a good idea. In fact, it's very easily done:
def variabledecode(func):
def newfunc(self, *args, **kwargs):
from formencode.variabledecode import variable_decode
return func(self, *args, **(variable_decode(kwargs)))
newfunc.func_name = func.func_name
newfunc.exposed = getattr(func, 'exposed', False)
return newfunc
I like this better since the variable decoding is now done before
validation, which is what I need. The real question is: Should expose
do this automatically, or else have a variable_decode option? That sure
looks easy to implement. Stripped down implementation:
def expose(html=None, validators=None,
template=None, format=None, content_type="text/html",
variable_decode=False): ## new
"""..."""
import controllers
if template:
html = template
def decorator(func):
def newfunc(self, *args, **kw):
tg_format = kw.pop("tg_format", format)
underscore = kw.pop("_", None)
cherrypy.response.headerMap['Content-Type']= content_type
errors = {}
## start new
if variable_decode:
kw = formencode.variabledecode.variable_decode(kw)
## end new
if validators:
if isinstance(validators, dict):
for field, validator in validators.items():
if field not in kw:
continue
try:
kw[field] = validator.to_python(kw[field])
except turbogearsvalid.Invalid, error:
errors[field] = error
else:
try:
kw = validators.to_python(kw)
except turbogearsvalid.Invalid, error:
errors = error.error_dict
if errors:
if hasattr(self, "validation_error"):
output = self.validation_error(func.func_name, kw,
errors)
else:
raise turbogearsvalid.Invalid(str(errors))
else:
output = func(self, *args, **kw)
return controllers._process_output(tg_format, output, html)
newfunc.func_name = func.func_name
newfunc.exposed = True
return newfunc
return decorator
I've tested this, i.e.:
@turbogears.expose(
variable_decode=True,
validators=dict(
...
),
)
def add(self, action=None, **kwargs):
...
and it seems to work.