Better Form Generation with Mako and Pylons
If you're a web developer in New York City, unless you work here, here or maybe here, you're probably not using Python for primary development (if you are, please post your company here). Since I'm not a PHP monkey or a Microsoftie, everywhere I work they're using Struts, which is where the dust has settled around Java Server Pages (with honorable mentions to Spring MVC and JSF).
I'm in a position where I might be able to push Python technology for a project or two, which have until now been built on Struts 1, the most common version of Struts even though Struts 2 is considerably nicer. So I wanted to see where Pylons is at with form rendering and processing these days. Most importantly, I wanted to ensure that layout is accomplished plainly within a template with no module-embedded HTML and no "magic generation" of forms from classes or other datastructures, and that the cycle of data from form to controller back to form again is similarly simple and obvious. Most projects in New York are the kind that get handed off to totally different people when complete, or even just 80% complete since you've been put onto something else, for remaining development and future enhancements. Code handoffs are extremely common, so it can't be overstated how much more important it is to be obvious than to be DRY. This is a big reason that tedious, plodding approaches like Struts and PHP are so popular - through raw verbosity they discourage opaque ways of doing things. Overly clever, data-driven rendering solutions that nobody can understand or extend (and are usually broken, anyway) are basically what gets thrown away and rewritten by the next team.
In developing Mako, a primary goal was to make a super-nice version of a particular "component" pattern which I had used for years primarily with HTML::Mason which for me provides a "sweet spot" of obviousness, agility, and succinctness. The focus is around the ability to create "tag libraries" which interact easily with a server-parsed templating language, and which can be implemented within templates themselves. In JSP development, taglibs are now the standard way to indicate dynamic areas of templates, but while they look pretty clean, they are painful to implement (requiring HTML embedded in hand-crafted classes, a few dozen XML pushups for every tag you add, and the obligatory application restart whenever they change), and the EL and OGNL expressions which are standard within taglibs interact terribly with straight Java code.
Mako allows the creation of tags which can be arbitrarily nested and interactive with one another via the <%def> construct, in combination with the <%call> tag. It's been my observation that the <%call> tag as well as the usage of nesting-aware <%defs> hasn't caught on yet, as the examples in the docs are a little dense, so here I will seek to demystify it a bit.
Pylons currently recommends a decent approach to rendering forms, using Form Tags which are essentially little functions you can embed in your template to render standard form elements. The handling of the form at the controller level uses FormEncode and routes validation errors through htmlfill. My approach modifies this to use Mako tags to build a site-specific taglib around the webhelpers tags and adds an explicit interaction between those tags and the controller, in a manner similar to a Struts form handler, which replaces htmlfill and allows all layout, including the layout of validation error messages, using the same template system. It also adds a preprocessor that illustrates how to build custom tags in Mako which look as nice as the built-in ones.
A tar.gz of the approach, which at this point should be regarded strictly as a proof of concept, can be downloaded here (works against Pylons 0.9.7), which contains three templates each illustrating a different approach to laying out the form. The three approaches are raw webhelpers with htmlfill, the <%call> tag approach, and finally using the "custom tag" approach. The final result, present in the file templates/mako_helpers.html, looks like this:
<%namespace name="form" file="/form_tags.mako"/>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Mako Form Helpers</title>
<link rel="stylesheet" href="/style.css" type="text/css" />
</head>
<body>
<h3>Using Mako Helpers</h3>
<%form:form controller='comment' action='mako_post'>
<table>
<tr>
<th colspan="2">Submit your Comment</th>
</tr>
<tr>
<td>Your Name:</td>
<td><%form:text name="name"/></td>
</tr>
<tr>
<td>How did you hear about this site ?</td>
<td>
<%form:select name="heard">
<%form:option value="" selected="True">None</%form:option>
% for desc, value in c.heard_choices:
<%form:option value="${value}">${desc}</%form:option>
% endfor
</%form:select>
</td>
</tr>
<tr>
<td>Comment:</td>
<td><%form:textarea name="comment"/></td>
</tr>
<tr>
<td colspan="2"><%form:submit/></td>
</tr>
</table>
</%form:form>
</body>
</html>
Where of note are the Mako-like <%form:foo> tags that aren't part of Mako ! A short preprocessor is applied to the source file which turns a tag like <%form:foo> into <%call expr="foo()"> at template compile time. I.e., a tag used above as:
<%form:textarea name="comment"/>
is preprocessed into raw Mako code:
<%call expr="form.textarea(name='comment')"/>
The %call tag invokes the textarea def inside the form namespace, which is defined in the file form_tags.mako. The def for textarea looks like:
<%def name="textarea(name, default=None, **attrs)">\
<%doc>
Render an HTML <textarea></textarea> tag pair with embedded content.
</%doc>\
${form_errors(name)}\
${h.textarea(name, content=request.params.get(name, default), **attrs)}\
</%def>
Above, the $form_errors(name) is a def call used for reporting validation messages. The point of form_tags.mako is that all the form tags and their layout is plainly visible and easily customized. Multiple versions of the file can be used in one application, providing different form layouts for different areas. The fact that it uses the h helper to render the actual HTML for each form control is also arbitrary; you could just as well implement the <textarea> source directly within the def if some special treatment were needed.
The demo also contains a modified version of Pylons' @validate decorator. Usage is mostly the same, except the form parameter is replaced by the more direct input_controller parameter, which is the method used for input:
from formhelpers.lib.mako_forms import validate as mako_validate
class CommentController(BaseController):
def index(self):
return render('/mako_helpers.html')
@mako_validate(schema=CommentForm, input_controller=index)
def mako_post(self):
c.name = self.form_result['name']
return render('/thanks.html')
Where above, mako_post hits the validator, and on an invalid exception the index controller method is called instead. Validation errors are placed in self.form_errors as well as c.form_errors for template access. The validator as well as the preprocessor are defined in lib/mako_forms.py.
That's pretty much all there is to it at this point, a few folks on #pylons seem enthused about it, so perhaps this can be turned into something more formally available and/or recommended when implementing a Pylons/Mako application.