In this section we will create a more complicated Draco program. The program is part of a bulletin board system and allows the user to add a message to the system. A message consist of two parts, a title and a body, neither of which can be empty. The system keeps these messages in a database table.
Let's start with the data definitions for this program. We will need a
messages
database table with two columns, namely subject and a
body. While we're at it, it might be useful to add a primary key so
that we can uniquely indentify each message in the system. The following SQL
statement creates this table:
create table messages ( id integer auto_increment, subject char(200) not null, body text not null );
We will create this table in the Draco system database. The Draco system
database is a database that contains some tables that are used by Draco
internally. All these tables are prefixed with the string draco_
.
The system database can be used by the user too. In fact, the user is
encouraged to use it. Draco keeps one global connection and a
global cursor for this database. This connection can optionally be made
persistent. All this makes it very easy and efficient to use the system
database.
Before we can use the system database however, we need to configure Draco so that knows where to find it. Add the following lines to the configuration file draco.ini in the document root of the web application:
Database = 'mysql' DatabaseHost = 'hostname' DatabaseName = 'name' DatabaseUser = 'user' DatabasePassword = 'passwd'
Change the values of DatabaseHost
, DatabaseName
,
DatabaseUser
and DatabasePassword
to the values for the
database host, the database name, the database username and the password
respectively. These settings must point to an accessible database server. If
you want to use a PostgreSQL database server, set Database
to
'pgsql'
. Make sure not to leave out the quotes in the string fields,
Draco requires them! There is no need to restart your web server, Draco will
pick up the changes automatically. For a more detailed explanation of the
Draco configuration file, see chapter 7. The only important
thing right now is that we can connect to it.
Now create the database table messages
as defined above, using the
command line mysql
or psql
utilities.
Let's continue with our bulletin board system. We have already defined its
data structure. Now we will lay out its call structure. There are two pages
involved in the process of adding a message. One page, say
/addmessage.dsp contains a form to add a message. This form has an
action
attribute that point to another page, say
/checkmessage.dsp.
We start with the handler of /checkmessage.dsp. We already know that we can access GET and POST parameters using the args parameter passed to the handler function. We must check if all the required parameter (``subject'' and ``body'') are given. If not, we will redirect the user back to /addmessage.dsp, storing a message in the session that indicates the failure. Additionally, we will store the value of the parameter the user did provide in the session too. This way the user doesn't have to type it again, just because he forgot the other field. This process is called form feedback.
The code for the addmessage handler looks like this. The lines are numbered this time.
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. 6. u_add = '/addmessage.dsp' 7. 8. class MessageHandler(Handler): 9. 10. def checkmessage(self, path, args): 11. """ 12. Check the form from "addmessage.dsp". 13. """ 14. error = '' 15. if not args.has_key('subject'): 16. error = 'No subject!' 17. elif not args.has_key('body'): 18. error = 'No body!' 19. if error: 20. ns_add = session.namespace(u_add) 21. ns_add['error'] = error 22. ns_add.update(args) 23. response.redirect(u_add) 24. cursor = database.cursor() 25. cursor.execute(""" 26. insert into messages 27. (subject, body) values (%(subject)s, %(body)s) 28. """, args) 29. self['status'] = 'The message has been added successfully.'
The above code fragment introduces a bunch of new features. We will discuss them one by one.
The first thing you'll notice is that in lines 2-4 a set of objects are
imported from Draco subpackages. The imported objects are: session
,
response
and database
. These objects are all global instances
of Draco objects that are created and kept up to date by Draco. Almost all
functionality that you as a developer can use within Draco is provided like
this in a global class instance. In this case, the session
object
provides access to the session management, the response
object
provides access to the http response and the database
object provides
access to the Draco system database.
In the function body in lines 14-18, the args namespace is checked if it contains the right variables. If not, the variable error is set. If error is set, the lines 20-22 take care of the form feedback. First, at line 20 a session subnamespace is create with scope ``/addmessage.dsp''. We could have used the session namespace directly, but this way we make sure that we don't interfere with possible left over variables from other forms. The variable error is added to this subnamespace at line 21, while line 22. adds the whole of the args namespace to the session subnamespace at once. Finally, line 23 redirects back to the form.
If error is not set, a cursor is obtained for the system database at line 24. We insert the new message in the ``messages'' table at lines 25-28 and at line 29 we set a message in the interface indicating of the success.
What happens to the form feedback, we will see later on. First we look at the the checkmessage.dsp template, which is very simple:
1. <html> 2. <head> 3. <title> Succes! </title> 4. </head> 5. <body> 6. <h1> Success </h1> 7. <p> <%= status.encode('html') %> 8. </body> 9. </html>
There are two new features in this template. The first is that we don't use
the <%
marker to delimit an embedded code block, but the <%=
marker. This marker denotes an expression. As opposed to a regular code
block, which writes output via the print
statement, an expression
block contains a single Python expression that is evaluated and the
resulting value is substituted back into the template.
The second new thing we notice is that we use the nonstandard ``html'' encoding to encode the success variable. This encoding is defined by Draco and is available to all handlers and template. It escapes characters that are illegal in html (<, > and &).
Now we go back to the form feedback. The handler of the /addmessage.dsp template follows. The surrounding Handler class is left out.
1. def addmessage(self, path, args): 2. """ 3. Show a form to add a message. 4. """ 5. ns_add = session.namespace(u_add) 6. self.update(ns_add) 7. ns_add.clear()
There are no new features here. We notice that the same session subnamespace that we used to store our form feedback variables, is accessed at line 5. The entire subnamespace is copied in the interface (line 6) and subsequently cleared.
The last piece of our message adding program is the /addmessage.dsp template:
1. <html> 2. <head> 3. <title>Add your message here</title> 4. </head> 5. <body> 6. <%= status.encode('html') %> 7. <p> Please enter your message here. 8. <form action="/checkmessage.dsp" method="post"> 9. <p> The subject: <br> 10. <input type="text" name="subject" 11. value="<%= value.encode('attr') %>" size="30"> 12. <p> The body: <br> 13. <textarea name="body><%= body.encode('html') %></textarea> 14. <p> <input type="submit" value="Add Message"> 15. </form> 16. </body> 17. </html>
At line 6, the message is inserted into the template. This is correct even
if there is no variable status because undefined variables are
initialized to empty strings by Draco. At lines 10 and 12 we see that the
values of the subject
and body
form elements are fed back in
the html. The value of the <input>
element is specified inside its
value
attribute, so we use the ``attr'' encoding here. The value of
the <textarea>
is in a regular html context so the ``html'' encoding
is used there.
This concludes our first, real life program. If you found the process of form feedback tiresome, you are absolutely right! In the next example of this section we will show how to use the Form and FormRewriter utility classes to save you a lot of work.