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 translatedcontent = 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:
myform.myt
basic formUsername: Age: &>