building web pages with python index page

Creating the First Page

Introducing Jinja

Believe it or not, there's already enough information to create the first web page -- a simple index page. In order to display a page, though, we need to create a template of it first. This is where Jinja comes in.

Create a directory under notebook.demo called tpl (for templates):

mkdir tpl

Jinja, like Django templates, has the ability to inherit parent templates. We'll start by creating a master template called base.html:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <link rel="stylesheet" href="/static/styles/style.css" />
  <title>{% marker "title" %}</title>
</head>
<body>
    <div id="header">
        <h1><a href="/" title="notebook">notebook</a></h1>
    </div>
    <div id="content">
      {% marker "content" %}
    </div>

      {% marker "sidebar" %}

</body>

This looks exactly like an outline of a normal HTML page -- and that's exactly what it is. The only difference is the addition of some tags enclosed with {% %}. These are the template tags that can be used in the child template.

Next, we'll create a second template, index.html:

{% extends "base" %}
{% marker "title" set "Notebook" %}

{% block "content" %}
    <ul class="add">
      <li><a href="/add/note">New Note</a></li>
      <li><a href="/add/category">New Category</a></li>
    </ul>

    <h3>Existing Notes</h3>
    <ul class="existing">
        {% for n in note %}
            <li><a href="/view/note/{{ n.note_id }}">{{ n.note_title }}</a></li>
        {% endfor %}
    </ul>
{% endblock %}

{% block "sidebar" %}
<div id="sidebar">
    <h3>Categories</h3>
    <ul class="sidelist">
        {% for c in cat %}
            <li><a href="/view/cat/{{ c.category_id }}">{{ c.category_category }}</a></li>
        {% endfor %}
    </ul>
</div>
{% endblock %}

Notice how we've extended from base.html in the first line. Also notice the additional block tags that extend the marker tags from base.html. Finally, there's the addition of some for loops with variables enclosed in {{ }} tags.

Creating the Main Application

Now that the templates are made, it's time to print them out. To do that, it's time to create the main application file, app.py.

from jinja import Template, Context, FileSystemLoader
from colubrid import WebpyApplication, Request, execute, HttpResponse
from models import *

class index(object):
    def GET(self):
        t = Template('index', FileSystemLoader('tpl'))
        c = Context({
            'cat': getCategories(),
            'note': getNotes()
        })
        return HttpResponse(t.render(c))

class Notebook(WebpyApplication):
    urls = [
        (r'^/$', index),
    ]

    slash_append = True

app = Notebook
if __name__ == '__main__':
        execute(reload=True, debug=False)

Again, a lot of information here, but still digestible. Like any other Python program, it starts by importing some modules. Here, it's importing the Jinja and Colubrid modules.

An index class is created with a GET method. This method will trigger anytime the webserver receives a GET request.

Next, a Notebook class is created. Notebook inherits from Colubrid's WebpyApplication. The urls is a list of regular expression to class mappings. Basically, in this only entry, Colubrid will pass a request for / to the index class.

slash_append is set to true to save us a lot of trouble. Basically, it will add an ending / to all of our URLs if there isn't one.

Finally, an instance of Notebook called app is created. app is given some information on where to find static files. In this case, anything in the static directory (which will be created below) will not be processed by Colubrid or Python. The last lines of the code define how the app will be executed. The reload parameter tells Colubrid to watch out for any changes to the files related to Colubrid, and if it sees a change, reload the whole application automatically. This is so you don't have to stop and start the test server.

Now we'll take a look at the index class in more detail. The t and c variables are used to compile the templates we created. t looks for index.html inside the tpl directory. c take a mapping of variables from the getCategories() and getNotes() functions (which we'll create in a second) and assigns them to cat and note. Therefore, inside the index.html template we can use these variables and their contents.

getCategories() and getNotes() will be created inside the models.py file. They will be shortcut queries to do just what they describe -- select all the Categories and Notes and return them.

def getCategories():
    r = select([category])
    return r.execute().fetchall()

def getNotes():
        r = select([note,category], 
            and_(note.c.category_category_id==category.c.category_id))
        return r.execute().fetchall()

Now for some final touches. If you look in base.html, you'll see a style sheet. For the time being, we'll use a blank one. Create the following from the root of notebook.demo:

mkdir -p static/styles
touch static/styles/style.css

Also, favicon.ico will become nothing but a huge pain until your web browser finds it. Just create a blank file called favicon.ico in the root of notebook.demo:

touch favicon.ico

To add in support for the static files, include this with the other import statements:

from colubrid.server import StaticExports

Next, add this underneath app = Notebook

app = StaticExports(app, {
    '/static': './static'
})

With everything in place, we should be ready to start the server. At the root of notebook.demo, run this:

python app.py

If everything is correct, you should see

serving on localhost:8080

If you do, open up your web browser to localhost:8080 and view your lovely index page!

References and Documentation for Part 3

I'm trying teach as much as I can through this tutorial, but I really don't want to just regurgitate documentation already written. Please read the following docs before moving on to get a better understanding of what's going on:

Jinja Docs

Colubrid Docs

Web.py Tutorial