3.4 Building a Useful Application

In the previous section we saw how Albatross tags can be used to remove presentation logic from your application. In this section we will see how with a simple program we can serve up a tree of template files.

If you look at the output of the simple4/simple.py program you will notice the following lines:

REQUEST_URI     /cgi-bin/alsamp/simple4/simple.py
SCRIPT_FILENAME /usr/lib/cgi-bin/alsamp/simple4/simple.py
SCRIPT_NAME     /cgi-bin/alsamp/simple4/simple.py

Now watch what happens when you start appending extra path elements to the end of the URL. Try requesting the following with your browser: http://www.object-craft.com.au/cgi-bin/alsamp/simple4/simple.py/main.html.

You should see the following three lines:

REQUEST_URI     /cgi-bin/alsamp/simple4/simple.py/main.html
SCRIPT_FILENAME /usr/lib/cgi-bin/alsamp/simple4/simple.py
SCRIPT_NAME     /cgi-bin/alsamp/simple4/simple.py

The interesting thing is that Apache is still using the simple4/simple.py program to process the browser request. We can use the value of the REQUEST_URI environment variable to locate a template file which will be displayed.

The sample application in the samples/templates/content1 directory demonstrates serving dynamic content based upon the requested URI. The program can be installed in your web server cgi-bin directory by running the following commands.

cd samples/templates/content1
python install.py

The CGI program content.py is shown below.

#!/usr/bin/python
import os
from albatross import SimpleContext, TemplateLoadError

script_name = os.environ['SCRIPT_NAME']
request_uri = os.environ['REQUEST_URI']
page = request_uri[len(script_name) + 1:]
if not page:
    page = 'main.html'

ctx = SimpleContext('templ')
ctx.locals.page = page
try:
    templ = ctx.load_template(page)
except TemplateLoadError:
    templ = ctx.load_template('oops.html')

templ.to_html(ctx)

print 'Content-Type: text/html'
print
ctx.flush_content()

To demonstrate this application we have three template files; main.html, oops.html, and other.html.

First main.html.

<html>
 <head>
  <title>Simple Content Management - main page.</title>
 </head>
 <body>
  <h1>Simple Content Management - main page</h1>
  <hr noshade>
  This is the main page.
 </body>
</html>

Now other.html.

<html>
 <head>
  <title>Simple Content Management - other page.</title>
 </head>
 <body>
  <h1>Simple Content Management - other page</h1>
  <hr noshade>
  This is the other page.
 </body>
</html>

And finally the page for displaying errors; oops.html.

<html>
 <head>
  <title>Simple Content Management - error page.</title>
 </head>
 <body>
  <h1>Simple Content Management - error page</h1>
  <hr noshade>
  <al-if expr="page == 'oops.html'">
   You actually requested the error page!
  <al-else>
   Sorry, the page <font color="red"><al-value expr="page"></font>
   does not exist.
  </al-if>
 </body>
</html>

Test the program by trying a few requests with your browser:

http://www.object-craft.com.au/cgi-bin/alsamp/content1/content.py
http://www.object-craft.com.au/cgi-bin/alsamp/content1/content.py/main.html
http://www.object-craft.com.au/cgi-bin/alsamp/content1/content.py/other.html
http://www.object-craft.com.au/cgi-bin/alsamp/content1/content.py/error.html
http://www.object-craft.com.au/cgi-bin/alsamp/content1/content.py/oops.html

Let's analyse the program step-by-step. The preamble imports the modules we are going to use.

#!/usr/bin/python
import os
from albatross import SimpleContext, TemplateLoadError

The next part of the program removes the prefix in the SCRIPT_NAME variable from the value in the REQUEST_URI variable. When removing the script name we add one to the length to ensure that the "/" path separator between the script and page is also removed. This is important because the execution context load_template() method uses os.path.join() to construct a script filename by combining the base_dir specified in the constructor and the name passed to the load_template() method. If any of the path components being joined begin with a "/" then os.path.join() creates an absolute path beginning at the "/".

If no page was specified in the browser request then we use the default page main.html.

script_name = os.environ['SCRIPT_NAME']
request_uri = os.environ['REQUEST_URI']
page = request_uri[len(script_name) + 1:]
if not page:
    page = 'main.html'

The next section of code creates the Albatross execution context and places the requested filename into the page local attribute. It then attempts to load the requested file. If the template file does not exist the load_template() will raise a TemplateLoadError exception. We handle this by loading the error page oops.html.

The error page displays a message which explains that the requested page (saved in the page variable) does not exist.

ctx = SimpleContext('templ')
ctx.locals.page = page
try:
    templ = ctx.load_template(page)
except TemplateLoadError:
    templ = ctx.load_template('oops.html')

Looking at the error page oops.html, you will see a new Albatross tag <al-if>.

  <al-if expr="page == 'oops.html'">
   You actually requested the error page!
  <al-else>
   Sorry, the page <font color="red"><al-value expr="page"></font>
   does not exist.
  </al-if>

The <al-if> tag allows you to conditionally include or exclude template content by testing the result of an expression. Remember that we placed the name of the requested page into the page variable, so we are able to display different content when the browser actually requests oops.html.

Finally, the remainder of the program displays the selected HTML page.

templ.to_html(ctx)

print 'Content-Type: text/html'
print
ctx.flush_content()