Templating for Web Database Interfaces

If you count the number of lines in the HTML files of the (corrected) v0.2 of the minimalist WSGI database interface I presented earlier, you’ll find 228 non-blank lines. However, there are only 91 unique lines. In other words, there is 60% repetition. Not a good example of the Don’t Repeat Yourself (DRY) principle.

In addition, film.py has seven lines with embedded HTML tags. It would be best if the HTML formatting code (i.e., the View code in MVC terms) resided all within the templates.

Therefore, it is time to replace the bare-bones templating scheme with something more full-featured.

Over the past few years, I have used or experimented with Cheetah, Django templates, Genshi, Kid, Mako, Mighty,  Zope’s TAL and perhaps others that don’t readily come to mind. I’ve settled on Mako primarily for the features I’ll explain below.

When creating database interfaces, particularly of the CRUD or administrative type, much of the repetition comes from defining input elements for each database attribute, e.g., <input>, <select> and <textarea> HTML elements. Moreover, the HTML form to input a new record may be almost identical —but not quite— to the form to update or display an existing record.

My solution to this DRY dilemma has been to use Mako’s <%def> tags, in conjunction with Mako namespaces, to create a library of HTML form “functions” that simplify the specification of input elements.

The following excerpt from templates/forms.html illustrates these concepts:

% if errors and name in errors:
  <div class="errmsg">${errors[name]}</div>
% endif
<%def name="text(name, label, size, value=None, readonly=False)">
  <label for="${name}">${label}: </label>
  <input type="text" id="${name}" name="${name}" size="${size}"\
${value and (' value="%s"' % unicode(value).replace('"', '&quot;')) or ''}\
${readonly and ' readonly="readonly"' or ''} />

The following is templates/film/fields.html which is used by both the new.html and edit.html templates to define the same three input fields:

<%namespace name="form" file="/forms.html"/>
<%page args="edit=False"/>
  <%form:text name="id" label="Id" size="10" value="${id}"
  <%form:text name="title" label="Title" size="64" value="${title}"/>
  <%form:text name="release_year" label="Release Year" size="10"

The templates/film/new.html file incorporates the above as follows:

<%namespace file="fields.html" name="fields"/>

If you run the application and press the Save button on the New Film page without entering anything, the three files above combine to generate the following HTML for the Id field:

<div class="form-row">
  <div class="errmsg">Id must be a positive integer</div>
  <label for="id">Id: </label>
  <input type="text" id="id" name="id" size="10" />

The errmsg <div> comes courtesy of the application passing a form ‘errors’ argument to the render() function and the opening %if in forms.html. You can also see that the text %def creates the form-row <div> as well as both the <label> and <input> elements. In edit.html, fields.body() is called with edit=True and as a result, the input element also gets a readonly attribute.

The forms.html “library” includes support for text, checkbox, select and textarea elements (the latter three are not currently used by this application). Counting unique vs. overall lines in the templates we now find only 25% repetition which is a good improvement over the original.

The complete code is available at GitHub, tagged as v0.2.1.

I’d like to take this opportunity to thank Mike Bayer for creating Mako templates and to encourage others to give them a try.

4 thoughts on “Templating for Web Database Interfaces

  1. Pingback: Joe Abbate: Templating for Web Database Interfaces | Python and PostgreSQL | Syngu

  2. Pingback: A Funny Thing Happened on the Way to the Webserver | Taming Serpents and Pachyderms

  3. Pingback: Dueling Frameworks, revisited | Taming Serpents and Pachyderms

  4. Pingback: Database User Interfaces – Pagination | Taming Serpents and Pachyderms

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.