Not pretending to be production-ready solution, just as an example. Actually I've copied most of decorator code from pylons.decorators:
from decorator import decorator
from webob import UnicodeMultiDict
class ModelException(Exception):
pass
def handle_exceptions(form):
def wrapper(fn, self, *args,**kwargs):
try:
return fn(self, *args, **kwargs)
except ModelException, e:
errors = str(e)
params = request.POST
is_unicode_params = isinstance(params, UnicodeMultiDict)
params = params.mixed()
request.environ['pylons.routes_dict']['action'] = form
response = self._dispatch_call()
# XXX: Legacy WSGIResponse support
legacy_response = False
if hasattr(response, 'content'):
form_content = ''.join(response.content)
legacy_response = True
else:
form_content = response
response = self._py_object.response
# If the form_content is an exception response, return it
if hasattr(form_content, '_exception'):
return form_content
form_content = htmlfill.render(form_content, defaults=params,
errors=errors)
if legacy_response:
# Let the Controller merge the legacy response
response.content = form_content
return response
else:
return form_content
return decorator(wrapper)
class HelloFormSchema(Schema):
allow_extra_fields = True
filter_extra_fields = True
name = formencode.validators.UnicodeString(not_empty=True)
email = formencode.validators.UnicodeString(not_empty=True)
class HelloController(BaseController):
def new(self):
return render('/hello/new.html')
def view(self):
return 'created'
@handle_exceptions(form='new')
@validate(schema=HelloFormSchema(), form='new')
@restrict("POST")
def create(self):
#here is code interacting with model which potentially could raise exception:
if self.form_result['name'] == 'doe':
raise ModelException('user already exists!')
return redirect(url(controller='hello', action='view'))
new.html:
${h.form(h.url(controller='hello', action='create'), 'post')}
<dl>
<dt>Name</dt>
<dd>${h.text('name')}</dd>
<dt>Email</dt>
<dd>${h.text('email')}</dd>
<dd>
${h.submit('create', 'Create')}
</dd>
</dl>
${h.end_form()}