You are not logged in Log in Join
You are here: Home » Members » heini1's Home » Formulator » Form-handling with Formulator and ZPT

Log in
Name

Password

 

Form-handling with Formulator and ZPT

 

Created by heini1 . Last modified 2005-12-02 12:51:53.

A small tutorial showing you how to handle forms in a very clean and efficient way using ZPT (Zope Page Template), the Formulator component and Python script.

In this example we’ll create a form where the user fills in his name, date of birth, e-mail address and chooses his favorite pet. After submitting, the user is directed either to a success page where his name and a thank-you message are displayed or, if some of the data is invalid or missing, to the form page again. In this case, the form-fields are displayed with the data entered by the user and error-messages are displayed in red beside the fields in question.

First of all, we create a Folder in which we’ll put all the different objects.
We then create a Page Template in this folder, called form_display for example, which will display our form. Enter the following code between the body tags:

<tal: define="global form here/form;
              global errors request/error_messages|nothing" />

<p>Please fill in the following form:</p>

<form name="form" method="post" action="form_handle">

  <table>
    <tr>
      <th align="left">name</th>
      <td>
        <tal:block content="structure python:form.user_name.render_from_request(request.REQUEST)" />
      </td>
      <td>
        <font color="red" tal:condition="python:errors['user_name']" 
                          tal:content="structure python:errors['user_name']"
                          tal:on-error="string:">
          error message
        </font>
      </td>
    </tr>

    <tr>
      <th align="left">date of birth</th>
      <td>
        <tal:block content="structure python:form.user_dateofbirth.render_from_request(request.REQUEST)" />
      </td>
      <td>
        <font color="red" tal:condition="python:errors['user_dateofbirth']" 
                          tal:content="structure python:errors['user_dateofbirth']"
                          tal:on-error="string:">
          error message
        </font>
      </td>
    </tr>

    <tr>
      <th align="left">e-mail address</th>
      <td>
        <tal:block content="structure python:form.user_email.render_from_request(request.REQUEST)" />
      </td>
      <td>
        <font color="red" tal:condition="python:errors['user_email']" 
                          tal:content="structure python:errors['user_email']"
                          tal:on-error="string:">
          error message
        </font>
      </td>
    </tr>

    <tr>
      <th align="left">favorite pet</th>
      <td>
        <tal:block content="structure python:form.user_pet.render_from_request(request.REQUEST)" />
      </td>
      <td>
        <font color="red" tal:condition="python:errors['user_pet']" 
                          tal:content="structure python:errors['user_pet']"
                          tal:on-error="string:">
          error message
        </font>
      </td>
    </tr>

    <tr>
      <td></td>
      <td><input type="submit" name="submit" value="submit information" /></td>
      <td></td>
    </tr>

  </table>
</form>

At the beginning of the page we define two variables: form and errors. The first one indicates the path to the Formulator object that we’ll create later on; the second one will contain a dictionary with the errors-messages or "nothing".
The action attribute of the form tag is set to form_handle, which is a Python script that we’ll create later.
Then, we have a table with different rows, one row for each form-field. A row contains an arbitrary name of the field (name, date of birth, e-mail address and favourite pet), a form-field that will be produced by the Formulator object (user_name, user_dateofbirth, user_email and user_pet) and an eventual error-message, which is generated by the Formulator object as well. And of course a submit button.

We now create our Formulator object. Add a Formulator Form to your folder and call it form for example. In this object, add the following fields:

  • a StringField called user_name;
  • a DateTimeField called user_dateofbirth, check the box which says "Display date only";
  • an EmailField called user_email;
  • a RadioField called user_pet, enter a few pet names in the Items field (return-key after each name) and set the Orientation to vertical.
This Formulator object will create the different form-fields and check them for errors. The error-messages of each field can be found and amended under the tab Messages. You can test a field under the tab Test.

We now create the page that will be displayed if the form has been filled in correctly. Create a new Page Template called form_success and paste the following code between the body tags:

Thanks <span tal:replace="options/user">Name</span>, we appreciate your help.

This page simply displays a thank-you message containing the users name.

We finally get to the Python script, which will handle the whole process. Add a Script (Python) to your folder and call it form_handle. Paste the following code in it (the Parameter List is empty):

"""
Checks request-data against a formulator object:
  - if the data is correct, a succes-page is displayed.
  - if the data is incorrect, a dictionary containing the errors is added to the Request and
the form page is displayed again with the original data and the error-messages. """ from Products.Formulator.Form import FormValidationError request = context.REQUEST response = request.RESPONSE formErrors = {} # check request-data against Formulator try: context.form.validate_all_to_request(request) # request-data contains errors except FormValidationError, e: for i in e.errors: title = i.field.get_value('title') text = i.error_text formErrors[title] = text request.set('error_messages', formErrors) response = context.form_display() # request-data contains no errors else: # do something with the request-data here response = context.form_success(user = request.user_name) return response

First, this script tries to validate the request-data against our Formulator object. If a FormValidationError is raised, which means if a field contained invalid data or was missing data, the title of the field in question (user_name, user_dateofbirth, etc) and the corresponding error-message is added to a dictionary called formErrors. This dictionary is then added to the request under the name error_messages and the form_display page is displayed again. If no FormValidationError has been raised, which means if the form has been filled in correctly (else statement), the form_success page is displayed with the users name added as an option. This is where you need to do something with the collected data (otherwise it’s pointless having a form). For instance, you can add the data to a database, send an e-mail or do whatever else.


Ok, here’s how the whole thing works.
When the form_display page is displayed, the variable errors is "nothing" since no request-object called error_messages exists. Our Formulator object called form produces the different form-fields, they’re empty since no request-data exists. No error-messages are displayed since errors is "nothing".
The user fills in the form, he makes a few mistakes. Let’s say the date entered doesn’t exist and his e-mail address is missing. When he pushes the submit-button, the script form_handle is invoked. It tries to validate the request-data but a FormValidationError is raised since the form contained errors. The field titles and error-messages are added to the formErrors dictionary, which is added to the request under the name error_messages. The form_display page is redisplayed. This time, the dictionary formErrors is assigned to the variable errors. The form-fields are rendered with the request-data and if an error-message exists for a particular field, this message is displayed in red beside the field. The user corrects his errors and pushes the submit button again. Our Formulator object validates the entered data, no FormValidationError is raised and the form_success page is displayed.
Et voilà.


I hope this small tutorial was comprehensive and helpful for your work. You can download the code of this example here in case I made some mistakes.

Othmar Heini