Handling Form data w/Formencode+SQLObject Redux

In a prior post on handling form data I covered how to easily populate a form using FormEncode/SQLObject and validate/save the data.

One of the things I noted was that I had to take the HTML form output first, and use htmlfill with it to populate it with defaults and errors before displaying. This required some extra lines I wasn’t to thrilled with in the controller. After an evening of chatting with Ian Bicking about whether FormEncode could somehow be cleaned up to make this easier, Ian suggested I use a Myghty feature I had forgotten about.

Component Calling with Content blocks in Myghty

Somewhat ironic since I use Myghty all day and Ian doesn’t (afaik). It’s a very useful feature called Component Calls with Content.
It’s probably easiest to understand the cool ability this provides by seeing it. In Myghty, each template is known as a component, and complex arrangements can easily be put together by component inheritance and component calls between them.

Normal Python function calls and expressions in a Myghty component (template) looks like this:


Hi everyone, 2 + 2 = <% 2+2 %> and your name is <% lookup_name() %>
</pre>

	

To include content from other components, you can either call the component through the function call m.comp('/some/template.myt') or you can use the component call syntax:


Hi everyone
  <& /sidebar.myt &>

So here’s how we will use a component call with content:


Hi, lets translate the content under:
<&| /translate.myt, lang='es' &>
    This entire block of content will be sent in as a single variable to translate.myt
    for use. This includes any <b>HTML tags and such</b> as well.
</&>

And the translate.myt template might look like:


The translated text is:


lang
%args>

from mytranslater import translated
content = m.content()
%init>

This is probably a lot to absorb, as it utilizes a few different Myghty concepts. Components can have arguments they expect, here the translate.myt component expects a lang arg which is passed in. The content block is then available as m.content() inside the template. The <%init> block is called first and variables defined there are available inside the template.

So how does this help us with the original problem?

Module Components

So far, all of these abilities are possible in Myghty’s ancestor, Mason. One more powerful construct available in Myghty however is the ability to not only call components, but Module Components.

I’ve been familiar with these for awhile as I use them for Controllers. It had never occurred to me though, that they can call any function in a module as if it was a component, not just ‘Controllers’.

Module components allow you to do a variety of interesting things, mainly, calling functions, classes, or objects that are in Python modules. Let’s take a look at the last translate.myt example using this approach instead.


Hi, lets translate the content under:
<&| MODULE:mylib:translate, lang='es' &>
    This entire block of content will be sent in as a single variable to translate.myt
    for use. This includes any <b>HTML tags and such</b> as well.
</&>

And the mylib Python module (assumed to be in the search path):


from mytranslater import translated

def translate(m, lang): body = “

The translated text is:

“ body += translated(lang, m.content()) m.write(body)

Myghty will examine the function signature to determine what variables it wants and will search the current scope to make sure they’re passed in. Very handy. :)

Putting it All Together

So let’s see how this helps us out, first, rather than having to take the form and render it in the controller, we’ll push this into the template using a component call with content. So our new myform.myt looks like:


	
  1. myform.myt


basic form



Username:

Age:



&>


defaults
errors
%args>

Our mylib module:


from formencode import htmlfill

def formfill(m, defaults, errors): form = m.content() m.write(htmlfill.render(form, defaults, errors))

And the new controller:


from ourschema import UserInfoSchema
from oursqlstuff import UserInfo

def mypage(m): errors, defaults = {}, m.request_args if m.request_args: form, errors = UserInfoSchema(), {} try: form_result = form.to_python(m.request_args) except api.Invalid, e: errors = e.unpack_errors() if not errors: UserInfo(**form_result) # database insert return m.subexec(’/thankyou.myt’) m.subexec('myform.myt’, defaults=defaults, errors=errors)

We’ve cut it down 1 line, from 12 to 11, however we no longer have to go through the slightly awkward maneuver of capturing the rendered form and running htmlfill over it in the controller. This is now handled easily in the template using a component call with content. The module component we use is extremely portable and can be used to wrap any form anywhere with automatic fill-in of errors and default values.

FormEncode 0.4 also deprecates HTMLForm which we were using before, so this will help out when thats no longer available.

Module component calls are quite useful, and make it easy to group together powerful function libraries that interact with Myghty parts. Being able to pass them content allows all sorts of new constructs to be created.

If you’re using Myghty with FormEncode and SQLObject, I’d highly suggest switching to this method instead of the one I blogged about before.