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 ofname
to lower case.{% raw %}{{name.title()}}{% endraw %}
convert the value to title case.{% raw %}{{name or "Default"}}{% endraw %}
use the value "Default" ifname
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 functiondefined
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>')
'<p>hello world</p>'
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 © 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.