Generating HTML Pages

A very common task in a web application is the generation of HTML output. Every application needs to generate HTML and should do so in a consistent manner so that all of the pages in the application follow the same structure and reference the same stylesheets etc. In the examples we've seen so far, the HTML code has been included in the Python code as literal strings. This is fine for simple applications but as they get more complex, so the need for separating out the task of page generation and system logic grows.

In the context of a Bottle application, the content returned by the procedure associated with a route is what is sent back to the browser and in most cases this will be some text including HTML markup. The simplest case is to include the text as a literal Python string.

@app.route('/')
def index():
    return "<h1>Hello World</h1>  <p>How are you?</p>"

The problem with this approach is that it mixes the content and layout of the application with the program code. In general we'd like to separate the HTML content from the program logic. One reason for this relates to the separation of concerns where we want to avoid mixing presentation layer code (HTML) with application logic (Python). Another requirement is to be able to modify the HTML that is generated by inserting different content in each page that is returned. This might be the result of a database query or other custom content generated by the application logic. The solution is to use templates that can be stored in separate files and encode just the presentation layer, and to refer to them from the program code.

The Bottle Simple Template Engine

The Bottle framework comes with a Simple Template Engine that provides a good solution to the problem of generating HTML content for web applications. The Bottle engine is quite powerful as it allows us to embed any Python code in the template to help generate the HTML required as well as just substituting the values of variables.

The simplest way to use the template library is just to substitute values into a string template in a similar way to string.template but with a different syntax. In this case variables are enclosed in double curly braces. Here's an example of experimenting with the library from the Python prompt:

>>> from bottle import template
>>> tpl = 'Hello {{name}}!'
>>> template(tpl, name='World')
u'Hello World!'

(Note, these examples are adapted from the Simple Template Engine documentation). Another difference here is that the template procedure takes named keyword arguments - in this example we pass the value for name. This is good for templates with a small number of values embedded but would become cumbersome with more. An alternative is to store the information in a Python dictionary and pass this as the second argument. Here's an example:

>>> from bottle import template
>>> info={'number': '123', 'street': 'Fake St.', 'city': 'Fakeville'}
>>> tpl = 'I live at {{number}} {{street}}, {{city}}'
>>> template(tpl, info)
u'I live at 123 Fake St., Fakeville'

Here we are still working with literal strings in Python code, but Bottle also allows us to put the template into a separate file and refer to it by name. By default, if we pass a template name to the SimpleTemplate and it will look for a file by that name in a subdirectory views with the extension .tpl. This single feature means that we can keep the HTML template code completely separate to the application code; a big win for the separation of concerns. To try an example of this we can create a file views/simple.tpl (views\simple.tpl if you are on Windows) with the content:

<html>
    <head><title>{{title}}</title></head>
    <body>
         <h1>{{title}}</h1>
         {{content}}
    </body>
</html>

This can now be used to render a page. Let's move on from command line examples to a real web application. We'll write this in a file main.py in the same directory as the views subdirectory with the following code:

from bottle import Bottle, template

app = Bottle()

@app.route('/')
def index():
    """Home page"""

    info = {'title': 'Welcome Home!',
            'content': 'Hello World'
            }

    return template('simple.tpl', info)

if __name__ == '__main__':
    app.run()

This example uses the template function again but this time the first argument is the name of the template file rather than the template itself. The template function is smart enough to work this out and treat the first argument differently. This is the most common way you will use templates in your application code. The second argument is a dictionary containing values that are used in the template; when you run the application, these are substituted into the page template directly and the page is served.

More Complex Templates

The Bottle Simple Template Engine is a powerful module and templates can contain more than just simple substitutions of variable values. In fact, they can contain any piece of Python code that you want to include using a slightly modified Python syntax. This section will outline some of these capabilities.

Using Variables

The simple examples above included the values of variables passed to the template using the double-curly-brace syntax. In fact this syntax can contain any Python expression that evaluates to a string (or can be coerced into one). Here are some examples of how this might be used:

  • {% raw %}{{name.lower()}}{% endraw %} convert the value of name to lower case.
  • {% raw %}{{name.title()}}{% endraw %} convert the value to title case.
  • {% raw %}{{name or "Default"}}{% endraw %} use the value "Default" if name is None (note that the variable still needs to be defined for this to work)
  • {% raw %}{{name if defined('name') else "Nothing"}}{% endraw %} uses the Bottle template function defined to test whether the variable exists and uses another value if not.

A very common case comes up when the value of a variable contains HTML markup. This might occur if we pass formatted content into the template. The default behavior of the template engine is to properly quote any HTML tags so that they appear in the page just as they would print out in Python. Here's an example:

>>> from bottle import template
>>> tpl = "{{content}}"
>>> template(tpl, content='<p>hello world</p>')
'&lt;p&gt;hello world&lt;/p&gt;' 

As you can see, the output from the template function contains entity references instead of the < and > characters. This means that when the page is rendered by the browser, the user will see this rather than the formatted paragraph. This is the safe thing to do since any content passed in to the template may have come from user input and so may contain malicious content. What we are guarding against is the insertion of Javascript code that could compromise the user's browser. However, there are many cases where we trust the content of the variable and want it to be included literally into the template. In this case we can use the ! character at the start of the template - this overrides the quoting of HTML tags:

>>> tpl = "{{!content}}"
>>> template(tpl, content='<p>hello world</p>')
'<p>hello world</p>'

Conditionals

You may want to have part of your template to vary based on some input variable. To achieve this you can use an if statement. For example, if we pass in a variable is_staff which is true if the user is a staff member, and we want to display a link to the admin site for those users. The template code would be:


% if is_staff:
<a href="/admin/">Admin Page</a>
% end

It is also possible to include an else clause to switch between different content based on an input variable. So if we want to include a subscription form for new users and a link to the profile for existing users we might say:

% if status=='registered':
<a href="/profile/">Your profile</a>
% else:
<form action="/subscribe/" method="POST">
    <input type="text" name="email">
    <input type="submit">
</form>
% end

Loops

A very common requirement in a template is to display a list of items in the same way. This is often to include the results of a database query in the page - for example, a list of blog posts, or shopping cart items. To do this we make use of the facility in Bottle templates to include Python code in a template. Let's look at a simple example that displays a list of names passed into the template. The Python code that calls the template is:

@app.route('/')
def index():
    """Home page"""

    info = {'title': 'Welcome Home!',
            'names': ['John', 'Paul', 'George', 'Ringo']
            }

    return template('simple.tpl', info)

The value of names in the dictionary passed to the template is a list of strings. We now need to write code in the template to display these as an unordered list. Here's the template code:

<html>
    <head><title>{{title}}</title></head>
    <body>
         <h1>{{title}}</h1>

         <ul>
          % for name in names:
             <li>{{name}}</li>
          % end
         </ul>

    </body>
</html>

The for loop in the template looks almost like a regular Python for loop. The % character is used to introduce Python code in the template and because we're mixing HTML code with Python we need an explicit way to mark the end of the for loop rather than using indentation as we would normally do; so we use the % end line. Anything inside the body of the loop will be repeated for each iteration of the loop, so in this case we'll get one list item for each name in the names list.

The content that is repeated can be as complex as required to display each item. A common case is where each item is itself a list of values - for example, values retrieved from a database query. For example, if we have a list posts which contains a blog posts pulled from a database, each entry in the list is itself a list containing the date, author name and the blog post content:

info['posts'] = [
         ('2007/03/13', 'Steve', '<p>Nothing happened today.</p>'),
         ('2007/03/12', 'John', '<p>Something happened today.</p>'),
         ('2007/03/11', 'Steve', '<p>Welcome to the new blog!</p>')
         ]

The third item in the list has been formatted as HTML and so needs to be inserted in the template without quoting. A template to render this might include:

 <div class='blogposts'>
     % for post in posts:
     <div class='post'>
         <h2>By {{post[1]}} on {{post[0]}}</h2>

         {{!post[2]}}
     % end

Here we use the three entries in each post to generate the content. post[0] is the date, post[1] is the author name and post[2] is the content that is inserted with a prefixed ! to prevent quoting of the HTML.

One very easy trap to fall into with Python code in Bottle templates is to forget to include the closing % end line in a loop. Unfortunately you won't get a warning about this and your template will render the content you send. What you will see is all of the content in the file after your % for line repeated for each item. This will look odd in the browser but will be very evident if you view the source code of the web page using your browser's developer tools.

Modular Templates

One of the motivations for using page templates is to make it easy to use the same page design over all of the pages in your application. You can achieve this by having a single template that contains the overall page layout HTML and that references your CSS stylesheet. However, it is common to have a few different page layouts within the same application; for example, the main page might have two columns while a user preferences page might contain a single column with a form. To achieve this it is common to modularise the page templates used in a project. One master template contains the main layout including say the menu bar, company logo and standard page footer that will be on any page. Other templates then specialise this to add new parts.

To achieve this, the Bottle template engine includes two functions: include and rebase that allow one template to be included in another. The simplest of these is include which just includes the result of rendering another template wherever it appears in a template. This might be used to include a standard menu bar in a page:

<html>
    <head><title>{{title}}</title></head>
    <body>
    % include('menu.tpl')

         <h1>{{title}}</h1>
...

The include function can also take keyword arguments to customise the rendering of the template within the page. While include is useful, it doesn't quite do the job that we want of allowing a common base layout to be used by all site templates. This is achieved by the rebase function which is like a reversed version of include: it allows a template to say that it should be included in another template to get the final result. To illustrate here is a simple example of a base template (save this as base.tpl):

<html>
<head>
  <link href='style.css' rel='stylesheet' type='css'>
  <title>{{title or 'No title'}}</title>
</head>
<body>
    <header>
        <ul class='menu'>
            <li><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
        </ul>
    </header>

  {{!base}}

  <footer>
      <p>Copyright &copy; 2015, Bob Bobalooba</p>
  </footer>
</body>
</html>

This is a simple template that uses a variable base with the ! character to prevent quoting of HTML content. The use of rebase is then in the actual template that we will call from our code (save this as 'index.tpl'):

% rebase('base.tpl', title='Page Title')
<p>Page Content using some {{variable}} values</p>

When we use the index.tpl template, it will first be rendered and then the result is inserted into the base.tpl template to get the final page. We can then have another template that also uses base.tpl, call this one error.tpl:

 % rebase('base.tpl', title='Page Title')
 <p>There has been an error: {{message}} values</p>

Our application might use this template to return an error page to the user. The advantage is that all pages will use the same overall page layout with the standard stylesheet, page menu and footer.

When you are designing a page layout for a website it is common to mock up your page design as a static HTML page - your web designer may well do this for you and deliver one or more HTML pages to you as the result of the design process. Your job is then to turn these into templates that your application can use to generate the final HTML. Using rebase it is possible to make a master template with most of the boilerplate of the design, leaving a placeholder for the content that will be generated by other templates and by your application code.