Thursday, May 23, 2002

More Page Templates (a buttal of sorts?)
Jon Udell and Zope Newbies both write stuff along the lines of I decided again not to use them because they would add more complexity to my sites and wouldn't solve any problems -- I'm not about to start using Dreamweaver or GoLive any time soon. It's just me and Pepper or Mozilla. I don't have to work with any designers, or that the learning curve is too steep.

Personally, I think that the learning curve for Page Templates is much smaller than DTML. The only problem is in trying to think like DTML when you really shouldn't be. While there are helpful conversion guides, I do think it best to approach ZPT on a fresh project rather than trying to retrofit them into something existing (by either adding on to a site/project, or trying to replace existing DTML).

Page Templates have a wonderfully clean syntax in the TAL/TALES combination. There are very few TAL attributes, and their operations and operational order are clearly defined. TALES eliminates much of DTML's guesswork by eliminating the whole "is it an expression? a literal? a name lookup?" game. The METAL macro expression system is a little more complicated, but ultimately provides a clean way of componentizing HTML/XML, and can be effectively used to eliminate the standard_html_header/footer idiom (I strongly dislike the whole header/footer inclusion idea proffered by so many templating languages, which require you to maintain balance between two separate files/objects to try to give some notion of "standard look" to a site. METAL is much more along the lines of Dreamweavers and GoLive's Templates idea, where a page has editable "slots" between commonly shared data, and can thus be edited as a whole page).

There are areas that Page Templates break down, especially in comparison with DTML. First, Page Templates are purely about HTML/XML. They're not good for dynamically generating style sheets, Javascript, or SQL, although they can be used to do so (but as the old maxim goes, just because you can doesn't mean you should). Second, DTML has a strong history as a reporting oriented templating language. TAL and TALES don't really take on this responsibility, and rather hand it off to other modules like ZTUtils (Zope Template Utilities), a woefully underdocumented piece of the pie. The Page Templates approach is ultimately better, I believe, because it's more open to new components being used to handle batching and useful widgets, while with DTML you got what was there. The 'dtml-tree' tag has been woefully stretched in order to allow many variations in how an expanding tree might find its data. ZTUtils offers handy utility modules, classes, and functions to replace all of this and allow a developer to better handle how it all might work, but it's currently much harder to do things like dtml-tree in Page Templates.

But DTML has suffered from many problems over its lifetime. Until Python Scripts came to Zope, it was the primary way most people learned how to program Zope. It grew a lot of programming language style muscle, while retaining all of its reporting language heritage. Its namespace stack is a powerful but often untamed beast, and its full of grotesque shortcuts and workarounds. Anyone who's seen _[_['sequence-item']] knows what I mean. And anyone's who's used _[_['sequence-item']] probably had a momentary twinge of "ugh!" before moving on. DTML has gotten the job done. There are places where it continues to get the job done - the SQL specific tags are one of my favorite things about doing SQL in Zope. But DTML is full of accidental surprises - a common mistake is due to the fact that <dtml-var someObj> and <dtml-var "someObj"> can produce wildly different results.

I've been using DTML since early 1997, and it was a joy when I first used it - there was no Zope, there was no through-the-web editing, there were no DTML expressions, there was just a clean templating language meant to be combined with Bobo published objects. All real logic happened in Python, and display oriented logic could happen in DTML. It worked out well. But, as DTML has grown new appendages in an ad-hoc manner over the years since then, it's become more and more of a wild card. Page Templates in Zope, combined with Python Scripts give us back a lot of that early power. I think there's less harm in trying them than avoiding them. They're truly one of the few really clean extensions to Zope - well designed, well maintained. Members of PythonLabs really got involved and did an excellent job ensuring Pythonic standards were met, and early contributors like Todd Coram (who introduced me to Ruby) did an equally good job of driving the design of TAL/TALES to be a fairly open design specification, of which Page Templates are merely an implementation.


9:49:20 PM  blips[]    

Zope SQL Traversal Joys
Zope has many delightful aspects that arise out of its very basic nature as a Web Object Publisher - that is, an ORB (Object Request Broker) that turns HTTP, FTP, WebDAV, and potentially other requests into Python object calls.

There's a really cool trick that arises out of Zope's SQL Methods and Acquisition, where you can go directly to a single result out of a relational database. It's generally known as direct traversal. What it does is basically turn URL Path pieces into parameters passed into a SQL Method. SQL Methods are part of Zope's relational database story - a separate DTML based SQL template (the SQL Method), posing as a callable Python/Zope object. SQL Methods make use of some extended DTML tags to ease dynamic SQL authoring. A simple, but rather powerful SQL Method might look like:

id: getPackage
params: package_id=""
 
SELECT package_id, name, price FROM PACKAGES
<!--#sqlgroup where-->
 <!--#sqltest package_id type=int multiple optional-->
<!--#/sqlgroup-->

When a 'package_id' parameter is passed, the entire 'sqlgroup where' block will render out into a full SQL 'Where' clause - if more than one value is passed in for 'package_id', the sqltest statement will even generate 'package_id IN ...' code. If no value(s) are passed in, the whole 'where' clause will be dropped (in this case - there's a lot of power in the dtml-sqlXXX tags).

The cool tricks come with direct traversal, where you can treat a SQL Result object like a local one. What this means is that a URL like:

../site/getPackage/package_id/2/menu.html

actually causes the getPackage SQL Method to be called, with the value '2' passed in to the package_id parameter. Traversal continues at this point, allowing menu.html to be acquired and rendered in the context of the result of 'getPackage'. Thus, if 'menu.html' were a Page Template, it could contain code like:

<h2 tal:content="here/name">Package Name</h2>
 
<p tal:content="here/price">package price...</p>
 
<a href="packages.html" 
    tal:attributes="href string:${container/absolute_url}/packages.html"
 >Return to package listing</a>

Notice the use of here, a page template context meaning the local context of where the template is being executed. The final line uses the 'container' context, which is where the template physically resides in Zope, to render a URL to get back to the package listing (placing the value of the TALES expression in the 'href' attribute of the anchor tag).

Since this example SQL Method has only one parameter, it can actually be configured to do Simple Direct Traversal, whereby the URL could be shortened down to:

.../site/getMenu/2/menu.html

and Zope would match the first value after the SQL Method in the URL to its 'package_id' parameter. Or, you could have URL's with many parameters, such as:

.../site/getMenu/menu_id/2/restaurant_id/4/menu.html

Which would pass in '2' to the 'menu_id' parameter, and '4' to a notional 'restaurant_id' parameter.

There's even more power that can be reaped by using Brains, classes (Python or ZClasses) that can be mixed in with the result to add extra computing power (or other tricks one might figure out to make a simple O-R Mapping system). And ever more power beyond that.
5:25:33 PM  blips[]    

GoLive Editing TAL in QuickEdit Mode
GoLive Editing TAL in QuickEdit Mode

10:30:39 AM  blips[]