Instructional Technology Portfolio | Design and Development Tools | Lessons | Zope Lessons
Login   |  Résumé   |  IT Portfolio   |  Home

Forms Processing with Python

There are two main ways of processing Zope forms. One is with DTML methods and the other is with Python methods. Since the python way of doing things is not as well documented, I will attempt to show common DTML methods encountered along-side the python equivalent.

This example begins with the very basic case. You have a form, you need to do something with the input and return a resulting page. Some forms, you need to validate the data and re-display the form should the user enter invalid data. A good example of this is the logic behind a login form where you want to ensure the user entered something for both user name and password.

SQL method

For this form, we have a SQL method called verifyUser defined that will look up a user's email and password and return user record for sucess or no records for failure. This script looks like this:

select * from users where email = <dtml-sqlvar email type=string> and password = <dtml-sqlvar password type=string>

The DTML way

The DTML script for processing this form looks something like this:

<dtml-if "SESSION['isauthorized'] == 0"> <dtml-if start> <dtml-call "REQUEST.set('msg_error', '')"> <dtml-if "not _.has_key('email') or _.len(email) == 0"> <dtml-call "REQUEST.set('msg_error', msg_error + message_error(115))"> </dtml-if> <dtml-if "not _.has_key('password') or _.len(password) == 0"> <dtml-call "REQUEST.set('msg_error', msg_error + message_error(113))"> </dtml-if> <dtml-if "_.len(msg_error) == 0"> <dtml-in "verifyUser(email=_.string.strip(email), password=_.string.strip(password))"> <dtml-call "SESSION.set('userid', userid)"> <dtml-call "SESSION.set('isauthorized', 1)"> <dtml-call "SESSION.set('username', firstname + ' ' + lastname)"> <dtml-call "updateUserSession( lastvisit = lastvisit, userid = userid, n_sessions = n_sessions + 1)"> <dtml-if "SESSION.has_key('back_url')"> <dtml-return "RESPONSE.redirect(SESSION['back_url'])"> <dtml-else> <dtml-return "RESPONSE.redirect(_[root].absolute_url())"> </dtml-if> <dtml-else> <dtml-call "REQUEST.set('msg_error', msg_error + message_error(10))"> </dtml-in> </dtml-if> <dtml-if msg_error><p align="center"><font color=red><dtml-var msg_error></font></p></dtml-if> </dtml-if> <form action="." method="POST"> <input type="hidden" name="start" value="1"> <dtml-unless back_url> <dtml-unless "SESSION.has_key('back_url')"> <dtml-call "SESSION.set('back_url', REQUEST['HTTP_REFERER'])"> </dtml-unless> </dtml-unless> <center> <table border="0" cellpadding="5" cellspacing="0"> <tr> <th colspan=2>If you are a returning shopper, please enter<br> your e-mail address and password to login in. </th> </tr> <tr> <td align="right">E-Mail Address</td> <td align="left"><input name="email" value='<dtml-var email missing="">' size="30"></td> </tr> <tr> <td align="right">Password</td> <td align="left"><input name="password" type="password" size="20"></td> </tr> <tr> <td> </td> <td align="left" align="center"><input type="submit" value="Login" name="login:method"></td> </tr> </table> <p><input type="submit" value="New Shopper Registration" name="new:method"></p> </center> </form> <dtml-else> <dtml-return "RESPONSE.redirect(_[root].absolute_url())"> </dtml-if>

In the above script, we have to set a flag (start) to indicate this is the first time we're calling this form for display in order to skip all the verification code at the beginning. Because we want to be able to prompt the user again (and again) for user name and password, we roll both the verification logic and the form into one DTML document and this technique allows us to avoid a lot of extra DTML codding just to pass input parameters from script to script.

The Python way

When you process forms with Python, you can separate the processing logic from the form's content. Although you can do this with DTML as well, you would have a hard time redisplaying data previously entered without jumping through a lot of loops. Here is the form as it looks for processing by Python:

<form action="." method="POST"> <p>Please enter your e-mail address and password to login in.</p> <table> <td align="right">E-Mail Address</td> <td align="left"><input name="email" value="<dtml-var email missing="">" size="30"></td> </tr> <tr> <td align="right">Password</td> <td align="left"><input name="password" type="password" size="20"></td> </tr> <tr> <td> </td> <td align="left" align="center"><input type="submit" value="Login" name="verify:method"></td> </tr> </table> </form>

Note two points of interests about this form:

The equivalent Python script for processing this form looks something like this:

# checks db for email address and password. If found, updates current session to logged in and # makes note of user's visit in db request = container.REQUEST RESPONSE = request.RESPONSE session = request.SESSION # See if User provided correct credentials to log in msg_error = '' email = request.get('email','') password = request.get('password','') if email == '': msg_error = 'Please provide email in the form of user@somedomain.ext\n' if password == '': msg_error = msg_error + 'Passwords cannot be blank. Please provide password to login.\n' if len(msg_error) == 0: result = container.verifyUser(email=email,password=password) if len(result) == 0: msg_error = 'Could not verify credentials. Please check email address and retype password to try again.' else: session.set('userid',result[0].userid) session.set('isauthorized', 1) session.set('username', result[0].firstname + ' ' + result[0].lastname) container.updateSession( lastvisit = result[0].lastvisit, userid = result[0].userid, n_sessions = result[0].n_sessions + 1) if len(msg_error) <> 0: session.set('msg_error', msg_error) return container.index_html(context, request) else: back_url = session.get('back_url','/') if session.has_key('back_url'): session.delete('back_url') return RESPONSE.redirect(back_url)

Notice how much cleaner and simpler the Python based way of processing forms is! With the logic and the interface cleanly separated, this mechanism is much easier to maintain. Also, your non-technical web-site maintainers will not be so intimidated updating simple forms with the Python approach as they would be with the DTML approach above.

So what is Python's trick for re-supplying values when you have to display the form more than once to get the user's input? Simple, we tell Zope to render the form again, passing the REQUEST object (which has all the information from previous form submission) already populated. Just think, with DTML, you would have had to call <dtml-call REQUEST.set(.... for every input field had you split out the DTML verification logic from the form.

Another aspect to consider, because the Python language is so much cleaner and simpler than DTML, you also have much less code to wade through and debug than otherwise with DTML


mwlang@cybrains.net
Guest Login   |  Home