3.2 Something more complicated

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.