In this gist (it’s not even a patch, just some stuff I keep in my app’s lib/base.by) is a refactored @validate.
On the surface it’s just the same as the standard
@validate(form='edit') def update(self): ...
but it has been broken up into a number of controller methods, each of which is usable directly. So if you find the decorator inflexible, you can write your actions in the undecorated form
def update(self): try: self._parse(request, schema=MyForm()) except Invalid as e: return self._render_invalid(e, form='edit') ...
It’s more than just a stylistic change; in addition to the usual HTML form data it will also accept POST data sent in JSON and send back any validation errors in JSON too if the client so desires. The presence of JSON is recognized by an
sent_json() helper that looks to see if request’s route had a format parameter set to ‘json’ (see previous post) or if the Content-Type header is ‘application/json’; responses work similarly via an
accepts_json() helper, but checking the Accept header.
Throw in a
render_json() helper and here are idioms for actions that will quite happily accept and produce JSON or HTML. First the traditional, decorated look:
@validate(form='edit') def update(self): ... if accepts_json(): return render_json(a_json_serialisable_thing) else: return render('template')
def update(self): try: self._parse(request, schema=MyForm()) ... except Invalid as e: return self._render_invalid(e, form='edit') ... if accepts_json(): return render_json(a_json_serialisable_thing) else: return render('template')
The undecorated form is slightly longer — potentially addressed by sharing exception handling controller-wide — but it offers the intriguing opportunity to move some validation to the model &emdash; any
Invalid exceptions raised in the
try block will be rendered appropriately, regardless of where they came from.