Handling basic forms - Developer tutorials - Air
This tutorial demonstrates how to create an interactive web form using the Air system. The tutorial is aimed at Air developers and assumes some knowledge of Python, XML and XSL.
The tutorial is structured into four sections, the first three cover the technologies (Python, XML, XSL) involved in implementing a web form, the final section discusses the various ways to handle the information submitted.
Several files are referred to in the tutorial, the complete set is available to download in a zip archive, handling-basic-forms.zip.
Though developers may wish to create a seperate script for creating a web form, for the purpose of the tutorial a content managable page is used. So if you follow this tutorial using the files from the zip download, click on the "Code (Python)" attribute of the page and copy and paste in the content of the appropriate .py file.
The form in this tutorial will handle a common requirement for websites, the contact us form. In the advanced section there is also information provided about how to handle forms that accept files.
The fields for our form will be,
- Name
- Subject
- Enquiry
- File (Advanced)
Coding the form in Python
Very little code is required to implement a form. The form must be loaded, initialized, submitted data handled and the resulting form included in the pages XML content.
Code is also used to apply intelligent validation and to store valid submissions but this is discussed in the following chapters.
To load a form,
import os # Import the form class from getme.web import form # Load the form XML and return a form instance myForm = form( xmlURI=os.path.join( ev.PATH_TO_XML, 'local/form/contact-us.xml' ) )
Now that the form is loaded we need to initialise it and handle two cases,
- When the page is first loaded (no submitted data).
- When the user submits the form.
We do this using a simple if test,
# Initialise the form
myForm.initForm( self.request )
if self.request.getValue( 'submit' ):
# Populate the form with the submitted data and validate it
myForm.populateByRequest( self.request )
if myForm.validateForm( self.request ):
# Handle a valid submission
...
# Show the form
domList.append( myForm.domOut() )
Once the code is in place we need to create the form.
Defining the form with XML
The form class instance created in the previous section is typically created from an XML document (it is possible to create and modify forms dynamically but the subject is beyond the scope of this tutorial).
The XML structure for it is described fully in the DTD (see form.dtd in the zip archive for this tutorial), but simply it allows you to build a form in a simple heiarachy of,
- form
- fieldSet
- field
Each field is given a template value which determines how it is displayed to the user, in the case of the Subject which is a drop-down field a selection list is defined.
The form XML,
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form SYSTEM "../../../web/dtd/form.dtd">
<form
name="contact-us"
method="post"
action="/contact-us"
enctype="application/x-www-form-urlencoded"
>
<fieldSet name="yourEnquiry" legend="Your enquiry">
<field name="name" template="textSingle" required="1">
<label>Name</label>
</field>
<field name="email" template="textSingle" required="1">
<label>Email</label>
</field>
<field name="subject" template="dropDown">
<label>Subject</label>
<selection selectionType="single">
<selectionItem value="General">
<label>General question</label>
</selectionItem>
<selectionItem value="Product">
<label>Product question</label>
</selectionItem>
<selectionItem value="...">
<label>...</label>
</selectionItem>
</selection>
</field>
<field name="messageNote" template="note">
<label>Please enter your message in the box below.</label>
</field>
<field name="enquiry" template="textMultiple" styleLayout="vertical" required="1">
<label>Enquiry</label>
</field>
<field name="submit" template="buttonInput" buttonType="submit" styleLayout="none">
<label>Submit</label>
</field>
</fieldSet>
</form>
The form tag has the following attributes,
- name
- The name is used to identify and include the form in the XSL template.
- method
- The method specifies how data is sent to the form (get or post), if the form is being submitted to a page/script that uses a vanity (re-written) URL then the method should be a "post" as a "get" will break the re-write rules.
- action
- The action is the URL to where form submissions will be sent
- enctype
- The enctype specifies how data is encoded when submitted. For non-binary data we use "application/x-www-form-urlencoded", for forms that accept files (potentially binary data) we use "multipart/form-data", forms that accept files must use the "post" method.
The fieldSet tag is used to break longer forms down in to related sections, it has the following attributes,
- name
- The name is used to identify the field set.
- legend
- The legend appears as the heading (legend) for the field set.
The field tag is used to define the various elements including input fields within the form, it has the following attributes,
- name
- The name is used to identify the field.
- template
- The template specifies the XSL template that will be used to present the field to the user.
- required
- Specifying required as 1 makes the field behave as required i.e. if the form is submitted without a valid value in this field it will be returned with an appropriate error message, an indicator will also be shown against the field.
- styleLayout
- The style layout determines the relative positions of the label and corresponding input field.
The label tag within field should contain the text/html that will be used for the field label.
For static drop-down, radio buttons or checkboxes the data is provided through the selection and selectionItem tags.
The selectionType attribute is used to determine if the user can select a "single" item or "multiple" items.
Now that we have created our form (and stored the file on the server in the local XML folder) we need to include it in our XSL template.
Including the form in the XSL template
To include the form in the pages XSL first we have to import the form XSL templates using,
<xsl:import href="../../xsl/air/form/defaultForm.xsl"/>
this line should be included in the template before the xsl:output tag. To include the form we need to apply the form template to our form XML using,
<xsl:apply-templates mode="defaultForm" select="//form[@name='contact-us']"/>
at the point within the page structure where you want the form to appear. If you are following this editing a default page structure (xsl) then the line above can replace the line within the content division.
<xsl:copy-of select="$emptyNode" />
At this point it should be possible to view the page and the form will appear, if you submit the form it should also validate, though a valid submission will do nothing. So now we need to handle the submitted data.
Handling a valid form submission
We're going to look at two ways of handling a users form submission. First we going to look at storing the data in a table on the Air database (for the purpose of the tutorial we assume that a table already exists to accept the form data. Remember that for a simple transfer of data the form field names should match the table field names exactly).
To store the form data in a database table we use the following code,
# Get the data submitted to the form in a safe & prepared format preparedData = myForm.prepareRequest( self.request ) # Create a class instance for our table contactUsRecord = contactUs( dbi=self.dbi ) # Import the form data into the record contactUsRecord.importData( preparedData ) # Insert the record contactUsRecord.dbInsert()
(The database related code has been commented out in the zip archive .py files - uncomment it once you have set up the database tables.)
To email the form data we use the following code (replace the getme email addresses):
# Import the email library
from getme.web import email
# Create the email message
emailMessage = """
Name: %s
Email: %s
Subject: %s
Enquiry:
%s
""" % ( preparedData[ "name" ], preparedData[ "email" ], \\
preparedData[ "subject" ], preparedData[ "enquiry" ] )
# Send the email
email.sendEmail(
"getme.co.uk <no-reply@getme.co.uk>",
"ant@getme.co.uk",
"Contact us enquiry",
emailMessage
)
Once the data is stored and/or emailed we need to forward the user to a thank you page, to do this we use the following code,
# Import the navigate library from getme.web import navigate # Forward the user to the thank you page navigate.redirect( self.request, os.path.join( ev.SITE_ADDRESS, 'contact-thank-you' )
The next section looks at how to extend our form to support for a submitted file.
Handling file submissions (Advanced)
Handling files sent to a form requires a number of changes to the Python code and XML. We'll look at the XML changes first as the changes are is relatively simple.
To add a file field to the form we use the following XML,
<field name="fileId" template="file" required="1">
<label>Attachment</label>
<requestCode>value = self._requestFile( request, name )</requestCode>
</field>
Note: Remember that the form enctype must now be set to "multipart/form-data".
Once the XML is updated we now need to change the code to be aware of the file field. The air system takes care of handling the file automatically storing it in the fileStore table as a temporary file (active for 8 hours) and returning an ID.
On a valid submission all we need to do is set the temporary file not to expire and store the ID of the file. To do this we change the code as follows,
from air.fileStore import baseStore, baseStoreById
# Initialise the form
myForm.initForm( self.request )
if self.request.getValue( 'submit' ):
# Populate the form with the submitted data and validate it
myForm.populateByRequest( self.request )
if myForm.validateForm( self.request ):
# Get the data submitted to the form in a safe & prepared format
preparedData = myForm.prepareRequest( self.request )
# If a valid submission prevent the file from expiring
preparedRequest[ 'fileId'] = myForm.storeFile( 'fileId', self.request, \\
self.session, baseStore, baseStoreById, baseStore.PERMANENT )
# ...
# Show the form
domList.append( myForm.domOut() )
To send the file as an attachment to an email we use the following code,
# Create an attachment for the email
myAttachment = email.fileStoreAttachment( baseStoreById( preparedRequest[ 'fileId'] ) )
# Email the customer as before but with an attachement
email.sendEmail(
"getme.co.uk <no-reply@getme.co.uk>",
"ant@getme.co.uk",
"Contact us enquiry",
emailMessage,
attachmentList=[myAttachment,]
)
That's it for this tutorial, if you have any questions or comments please contact the author Anthony Blackshaw <ant@getme.co.uk>.
