This third and final example will show you how to use Draco's Form and FormRewriter classes to simplify the verification and feedback of html forms. We will adapt the example from the previous section to use these classes.
Let's start with the process of form verification. The verification process can be split up in three logical phases:
Draco provides the utility class Form that helps you to implement the three phase verification process described above. How to use it is best shown by an example. The following code fragment implements the checkmessage() handler from the previous section using a Form.
1. from draco.handler import Handler 2. from draco.session import session 3. from draco.response import response 4. from draco.database import database 5. from draco.util import attributes 6. from draco.form import * 7. 8. u_add = '/addmessage.dsp' 9. 10. class AddMessageForm(Form): 11. 12. def __init__(self): 13. Form.__init__(self) 14. self.addField(StringField('subject', absent='No subject!')) 15. self.addField(TextField('body', absent='No body!')) 16. 17. class MessageHandler(Handler): 18. 19. def checkmessage(self, path, args): 20. """ 21. Check the form from "addmessage.dsp". 22. """ 23. form = AddMessageForm() 24. try: 25. form.parse(args) 26. except FormError, err: 27. ns_add = session.namespace(u_add) 28. ns_add.update(attributes(err)) 29. ns_add.update(args) 30. response.redirect(u_add) 31. cursor = database.cursor() 32. cursor.execute(""" 33. insert into messages 34. (subject, body) values (%(subject)s, %(body)s) 35. """, form) 36. self['status'] = 'The message has been added successfully.'
There are a lot of new features here so let's discuss them one at a time. An important part is at lines 10-15, where a new form is defined. This is done by subclassing Form that was imported from draco.form at line 6. Then, in the constructor at lines 14,15, two objects are added to the form: a StringField and a TextField. This is how you build up forms: you add one or more Field subclasses. Each field corresponds to one input element and the type of the field specifies which values are acceptable. Draco provides an extensive set of Field subclasses in the draco.form module. Two field types are used here: StringField and TextField. They verify that their input is a single line string and an arbitrary text respectively.
The form is instantiated at line 23. At line 25, its parse() method is called to parse the args namespace. If no error occurs, we can access the processed arguments by using form as a namespace. This is shown at lines 32-35 where the message is stored in the database.
If the form cannot be verified, a FormError exception is raised.
Normally, you'd want to catch this exception and provide form feedback. The
exception object contains two attributes, status and
err_fields. The first attribute is a string describing the error. By
default this is a terse english error message but it can be customized using
illegal=
and absent=
keyword parameters in the Field
constructor. The second attribute, err_fields, contains a list of
fields names that are in error3.2. At line 28, we use the attributes() utility function
to extract these attributes and put them in a dictionary so that we can
store them all at once in the session. At line 29, the arguments that were
provided are stored in the session too and at line 30 the remote client is
redirected back to the form.
By adding Field to the form, you can implement phases 1 and 2 of the form verification process described above. To check dependencies between form fields and/or exernal data, you must provide your own parse() method. Typically, you'd first call Form's parse() method and then check your dependencies.
Let's close the subject of form verification and continue with form
feedback. In the example from the previous section we have substituted back
form variables by using <%=
and %>
markers to insert values
at strategic locations in the template. This process is rather tedious and
error prone. Fortunately Draco provides an automated way of doing this.
First we must introduce Draco's tag rewriting system. This system is
explained in detail in section 5.5 of this manual but for now
it is sufficient to know that you can register a special function to rewrite
html tags in a template. Each time a html tag is encountered, its registered
functions are run. Such functions are called rewriter functions and
can change the attributes of an html tag and can append some literal text.
This mechanism is used by Draco to implement url tagging, image size
detection, and also form feedback. Below is a new version of the
addmessage
handler that makes use of this. Only the handler is shown,
its enclosing handler class is left out.
1. from draco.form import * 2. 3. def addmessage(self, path, args): 4. """ 5. Show a form to add a message. 6. """ 7. ns_add = session.namespace(u_add) 8. self.update(ns_add) 9. ns_add.clear() 10. rewriter = FormRewriter(self) 11. rewriter.install()
At line 10 we instantiate a FormRewriter, passing the interface
(self
) as a parameter. This rewriter is registered with the rewrite
manager at line 11. The rewriter will look for form elements in the html and
will put back values with the same name from the namespace that we passed as
an argument to the constructor. Now, the /addmessage.dsp template
doesn't need to contain anymore code blocks, except the expression that
formats status.