Views

In the MVC paradigm the view manages the presentation of the model.

The view is the interface the user sees and interacts with. For Web applications, this has historically been an HTML interface. HTML remains the dominant interface for Web apps but new view options are rapidly appearing.

These include Macromedia Flash, JSON and views expressed in alternate markup languages like XHTML, XML/XSL, WML, and Web services. It is becoming increasingly common for web apps to provide specialised views in the form of a REST API that allows programmatic read/write access to the data model.

More complex APIs are quite readily implemented via SOAP services, yet another type of view on to the data model.

The growing adoption of RDF, the graph-based representation scheme that underpins the Semantic Web, brings a perspective that is strongly weighted towards machine-readability.

Handling all of these interfaces in an application is becoming increasingly challenging. One big advantage of MVC is that it makes it easier to create these interfaces and develop a web app that supports many different views and thereby provides a broad range of services.

Typically, no significant processing occurs in the view; it serves only as a means of outputting data and allowing the user (or the application) to act on that data, irrespective of whether it is an online store or an employee list.

Templates

Template rendering engines are a popular choice for handling the task of view presentation.

To return a processed template, it must be rendered and returned by the controller:

from helloworld.lib.base import BaseController, render

class HelloController(BaseController):
    def sample(self):
        return render('/sample.mako')

Using the default Mako template engine, this will cause Mako to look in the helloworld/templates directory (assuming the project is called ‘helloworld’) for a template filed called sample.mako.

The render() function used here is actually an alias defined in your projects’ base.py for Pylons’ render_mako() function.

Directly-supported template engines

Pylons provides pre-configured options for using the Mako, Genshi and Jinja2 template rendering engines. They are setup automatically during the creation of a new Pylons project, or can be added later manually.

Passing Variables to Templates

To pass objects to templates, the standard Pylons method is to attach them to the tmpl_context (aliased as c in controllers and templates, by default) object in the Controllers:

import logging

from pylons import request, response, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect

from helloworld.lib.base import BaseController, render

log = logging.getLogger(__name__)

class HelloController(BaseController):

    def index(self):
        c.name = "Fred Smith"
        return render('/sample.mako')

Using the variable in the template:

Hi there ${c.name}!

Strict vs Attribute-Safe tmpl_context objects

The tmpl_context object is created at the beginning of every request, and by default is an instance of the AttribSafeContextObj class, which is an Attribute-Safe object. This means that accessing attributes on it that do not exist will return an empty string instead of raising an AttributeError error.

This can be convenient for use in templates since it can act as a default:

Hi there ${c.name}

That will work when c.name has not been set, and is a bit shorter than what would be needed with the strict ContextObj context object.

Switching to the strict version of the tmpl_context object can be done in the config/environment.py by adding (after the config.init_app):

config['pylons.strict_c'] = True

Default Template Variables

By default, all templates have a set of variables present in them to make it easier to get to common objects. The full list of available names present in the templates global scope:

  • c – Template context object (Alias for tmpl_context)
  • tmpl_context – Template context object
  • config – Pylons PylonsConfig object (acts as a dict)
  • g – Project application globals object (Alias for app_globals)
  • app_globals – Project application globals object
  • h – Project helpers module reference
  • request – Pylons Request object for this request
  • response – Pylons Response object for this request
  • session – Pylons session object (unless Sessions are removed)
  • translator – Gettext translator object configured for current locale
  • ungettext() – Unicode capable version of gettext’s ngettext function (handles plural translations)
  • _() – Unicode capable gettext translate function
  • N_() – gettext no-op function to mark a string for translation, but doesn’t actually translate
  • url – An instance of the routes.util.URLGenerator configured for this request.

Configuring Template Engines

A new Pylons project comes with the template engine setup inside the projects’ config/environment.py file. This section creates the Mako template lookup object and attaches it to the app_globals object, for use by the template rendering function.

# these imports are at the top
from mako.lookup import TemplateLookup
from pylons.error import handle_mako_error

# this section is inside the load_environment function
# Create the Mako TemplateLookup, with the default auto-escaping
config['pylons.app_globals'].mako_lookup = TemplateLookup(
    directories=paths['templates'],
    error_handler=handle_mako_error,
    module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
    input_encoding='utf-8', default_filters=['escape'],
    imports=['from webhelpers.html import escape'])

Using Multiple Template Engines

Since template engines are configured in the config/environment.py section, then used by render functions, it’s trivial to setup additional template engines, or even differently configured versions of a single template engine. However, custom render functions will frequently be needed to utilize the additional template engine objects.

Example of additional Mako template loader for a different templates directory for admins, which falls back to the normal templates directory:

# Add the additional path for the admin template
paths = dict(root=root,
             controllers=os.path.join(root, 'controllers'),
             static_files=os.path.join(root, 'public'),
             templates=[os.path.join(root, 'templates')],
             admintemplates=[os.path.join(root, 'admintemplates'),
                             os.path.join(root, 'templates')])

config['pylons.app_globals'].mako_admin_lookup = TemplateLookup(
    directories=paths['admin_templates'],
    error_handler=handle_mako_error,
    module_directory=os.path.join(app_conf['cache_dir'], 'admintemplates'),
    input_encoding='utf-8', default_filters=['escape'],
    imports=['from webhelpers.html import escape'])

That adds the additional template lookup instance, next a custom render function is needed that utilizes it:

from pylons.templating import cached_template, pylons_globals

def render_mako_admin(template_name, extra_vars=None, cache_key=None,
                      cache_type=None, cache_expire=None):
    # Create a render callable for the cache function
    def render_template():
        # Pull in extra vars if needed
        globs = extra_vars or {}

        # Second, get the globals
        globs.update(pylons_globals())

        # Grab a template reference
        template = globs['app_globals'].mako_admin_lookup.get_template(template_name)

        return template.render(**globs)

    return cached_template(template_name, render_template, cache_key=cache_key,
                           cache_type=cache_type, cache_expire=cache_expire)

The only change from the render_mako() function that comes with Pylons is to use the mako_admin_lookup rather than the mako_lookup that is used by default.

Custom render() functions

Writing custom render functions can be used to access specific features in a template engine, such as Genshi, that go beyond the default render_genshi() functionality or to add support for additional template engines.

Two helper functions for use with the render function are provided to make it easier to include the common Pylons globals that are useful in a template in addition to enabling easy use of cache capabilities. The pylons_globals() and cached_template() functions can be used if desired.

Generally, the custom render function should reside in the project’s lib/ directory, probably in base.py.

Here’s a sample Genshi render function as it would look in a project’s lib/base.py that doesn’t fully render the result to a string, and rather than use c assumes that a dict is passed in to be used in the templates global namespace. It also returns a Genshi stream instead the rendered string.

from pylons.templating import pylons_globals

def render(template_name, tmpl_vars):
    # First, get the globals
    globs = pylons_globals()

    # Update the passed in vars with the globals
    tmpl_vars.update(globs)

    # Grab a template reference
    template = globs['app_globals'].genshi_loader.load(template_name)

    # Render the template
    return template.generate(**tmpl_vars)

Using the pylons_globals() function also makes it easy to get to the app_globals object which is where the template engine was attached in config/environment.py.

Changed in version 0.9.7: Prior to 0.9.7, all templating was handled through a layer called ‘Buffet’. This layer frequently made customization of the template engine difficult as any customization required additional plugin modules being installed. Pylons 0.9.7 now deprecates use of the Buffet plug-in layer.

See also

pylons.templating - Pylons templating API

Templating with Mako

Introduction

The template library deals with the view, presenting the model. It generates (X)HTML code, CSS and Javascript that is sent to the browser. (In the examples for this section, the project root is ``myapp``.)

Static vs. dynamic

Templates to generate dynamic web content are stored in myapp/templates, static files are stored in myapp/public.

Both are served from the server root, if there is a name conflict the static files will be served in preference

Making a template hierarchy

Create a base template

In myapp/templates create a file named base.mako and edit it to appear as follows:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    ${self.head_tags()}
  </head>
  <body>
    ${self.body()}
  </body>
</html>

A base template such as the very basic one above can be used for all pages rendered by Mako. This is useful for giving a consistent look to the application.

  • Expressions wrapped in ${…} are evaluated by Mako and returned as text
  • ${ and } may span several lines but the closing brace should not be on a line by itself (or Mako throws an error)
  • Functions that are part of the self namespace are defined in the Mako templates

Create child templates

Create another file in myapp/templates called my_action.mako and edit it to appear as follows:

<%inherit file="/base.mako" />

<%def name="head_tags()">
  <!-- add some head tags here -->
</%def>

<h1>My Controller</h1>

<p>Lorem ipsum dolor ...</p>

This file define the functions called by base.mako.

  • The inherit tag specifies a parent file to pass program flow to
  • Mako defines functions with <%def name=”function_name()”>…</%def>, the contents of the tag are returned
  • Anything left after the Mako tags are parsed out is automatically put into the body() function

A consistent feel to an application can be more readily achieved if all application pages refer back to single file (in this case base.mako)..

Check that it works

In the controller action, use the following as a return() value,

return render('/my_action.mako')

Now run the action, usually by visiting something like http://localhost:5000/my_controller/my_action in a browser. Selecting ‘View Source’ in the browser should reveal the following output:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
  <!-- add some head tags here -->
  </head>
  <body>

<h1>My Controller</h1>

<p>Lorem ipsum dolor ...</p>

  </body>
</html>

See also

The Mako documentation
Reasonably straightforward to follow
See the Internationalization and Localization
Provides more help on making your application more worldly.