3.4 Building a Useful Application

#
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 or os.path.dirname(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()