Templates

Templates are rendered by the view in order to present data to your users. Typically, you’ll be rendering an HTML page, but you may use templates for generating other things.

Ferris uses the Jinja2 template engine and adds a few helper functions and filters, some layouts, and a theme system.

Jinja2 is a complex template engine with many features, many of which are used by Ferris. It’s highly recommended to read through Jinja2’s documentation.

Conventions

Templates are stored in /app/templates/[controller]/[prefix_][action].html. So if you have a controller named Bears and an action named picnic, you would create a file named /app/templates/bears/picnic.html. If you have a controller named Pages, an action named view with a prefix of mobile, you would create the template at /app/template/pages/mobile_view.html.

Concepts

  • Views are used by the controllers to expose data and render a template.
  • Templates are files that contain the presentation logic for a particular action.
  • Layouts wrap templates in presentation code. This is great for creating a consistent look and feel. The layout should contain pieces that are used across views (such as the html skeleton, header, and footer) while the template should just contain code unique to the action.
  • Elements are small pieces of templates that are included to reduce code duplication.
  • Macros are files that contain macros (reusable functions) and pieces of more complex objects.
  • Themes are a collection of templates, macros, layouts, and elements that can override those in the default theme.

It can sometimes be difficult to visualize how all of these pieces work together. Consider the template to be the starting point; this is the first file the template engine examines when rendering. Let’s take a Posts controller that’s rendering the view action. The template will be templates/posts/view.html. Let’s assume the template looks like this:

{% extends "layouts/simple.html" %}

{% block header %}
{{post.title}}
{% endblock %}

{% block content %}
{{post.content}}
{% endblock %}

And the layout that it extends (simple.html) looks like this:

<html>
<body>
    <header>
        {% block header %}{% endblock %}
    </header>
    <section>
        {% block section %}{% endblock %}
    </section>
</body>
</html>

When this template is rendered the blocks are placed into the corresponding blocks in the layout. Here’s an image that visualizes that:

../_images/views-layouts.png

Templates

Templates can just be simple static html:

<div> Hello, world! </div>

However, you probably want to provide some sort of data:

<div> Hello, {{name}}</div>

Again see the Jinja2 documenation for more information on the syntax.

Usually, a template will inherit from a layout and provide one or more blocks with content:

{% extends "layouts/default.html" %}

{% block layout_content %}
    <div> Hello! </div>
{% endblock %}

Also, a template can inherit from any other valid template (which in turn may inherit from another template or layout):

{% extends "posts/list.html" %}

{% block post_title %}
    <h1 class='super-large'>{{post.title}}</h1>
{% endblock %}

Providing and Accessing Data

Data is provided in a controller via the dictionary self.context which is an alias to self.meta.view.context:

self.context['species'] = "Raxacoricofallapatorian"

This data can now be accessed by name in the template:

This is a {{species}}!

Of course, more complex data can be provided such as a Model instance:

self.context['species'] = Species.find_by_planet('Gallifrey')

Properties of that object can be accessed in the template:

The primary species of the planet {{species.planet}} is {{species.name}}.

Layouts

Layouts reside inside of app/templates/layouts and serve as the base templates for regular templates to inherit from.

For example, here’s a layout named large_text.html:

<h1>{% block content %}{% endblock %}</h1>

Here’s a template that inherits from it:

{% extends "layouts/large_text.html" %}

{% block content %}Yeah, Big Text!{% endblock %}

For more info on template inheritence, see the jinja2 docs on inheritance.

Ferris provides two standard layouts. You can use overloading as described below to customize these layouts.

The Default Layout

Located at layouts/default.html, the default layout provides a simple Twitter Bootstrap layout and a few blocks.

layout_head_title

Text inside of the <title> tag.

layout_head

Markup inside of the <head> tag, useful for adding meta tags and such.

layout_body

Everything inside the body tag, wraps layout_content, layout_before_content, and layout_after_content

layout_content

Content inside of the <div class='container'> tag, the common spot for overriding and placing content.

layout_before_content

Content before the <div class='container'> tag

layout_after_content

Content after the <div class='container'> tag

layout_scripts

Content right before the closing body tag. Useful for adding scripts and stylesheets.

The Admin Layout

Located at layouts/admin.html, the admin layout is used by Scaffolding and provides a layout with a side bar and navigation bar.

It contains all of the same blocks as the default layout, plus:

layout_navigation

Contains the side navigation.

layout_wrapper

Contains layout_header, layout_sidebar, and layout_content

layout_header

Contains the breadcrumb and sits between layout_nav_bar and the two columns.

layout_sidebar

Contains the action pallete (or any other content to be placed in the sidebar).

Elements

Elements are typically located in app/templates/elements or for very specific elements app/templates/[handler]/elements. There’s nothing special about element other than just the organization and the idea.

Take this element post-item.html for example:

<h1>{{post.name}}</h1>
<div>{{post.content}}</div>

And this template that uses the element:

<section>
{% for post in posts %}
    {% include "elements/post-item.html" with context %}
{% endfor %}
</section>

The code from the element gets placed in the template where it’s included and has access to the context. This is somewhat similar to the idea of blocks in layouts.

It sometimes helps to visualize this:

../_images/views-elements.png

Macros

Macros are usually located in app/templates/macros/[name].html, although some items choose app/templates/[name]/macros.html. The first format is used when the macro is for general purposes, while the second is used when macros are restricted to one set of templates.

Macros are files that contain a collection of jinja2 macros.

Built-in Macros

Ferris provides a handful of built-in macros to help in creating templates. Each of these are documented in their associated module.

Overloading

Templates can be overloaded. Overloading is different from inheritance – inheritance involves creating a new template that re-uses blocks from another template while overloading completely replaces the original template. This is very useful for customizing templates that are built into ferris.

For example, you’re probably going to create your own layouts. Ferris includes a default layout at layouts/default.html and it’s likely that during prototyping, your templates already inherit from this. Eventually you’ll be ready to put your own look and feel. You can copy ferris/templates/layouts/default.html into app/templates/layouts/default.html. The file in app folder will override the one in ferris and all templates that use {% extends 'layouts/default.html'} will now use the one in app. You can now customize it as needed.

You can use this to customize everything in Ferris’ templates - from layouts to macros to scaffolding.

Resolution Order

Templates are resolved by name in the following order:

  • First, the theme is taken into account. This whole resolution order is first applied to the theme folder if the theme is set.
  • app/templates
  • plugins/[plugin]/templates
  • ferris/templates
  • If using scaffolding, Ferris will check scaffolding/[action].html. This can be overwritten. Simply copy the scaffolding folder into app/templates and see Scaffolding for more details.

For example if you render posts/view.html Ferris checks for that template in each of those folders from top to bottom and uses the first one it finds. Notice that app takes precedence over everything else; this means you can override any template from Ferris and any plugins inside of your app.

Prefixed Paths

Also note that you can use prefixed paths to explicitly access un-overloaded templates. For example if I wanted the layout that’s in ferris and not in app I can use ferris/layouts/default.app. The following prefixes are available:

  • ferris - maps to /ferris/templates
  • app - maps to /app/templates
  • [plugin]/ - maps to /plugins/[plugin]/templates

Themes

Themes are a collection of templates, elements, and macros that can override those in the root or default theme. Themes are located in app/templates/themes/[name]. Their directory structure mirrors that of the root app/templates folder.

For example, if you have a root template structure like this:

* posts
    * list.html
    * view.html
* elements
    * post.html

And you created a new theme called mobile under app/templates/themes/mobile with the following directory structure:

* posts
    * view.html
* elements
    * post.html

If you switch the theme to mobile, then the template engine will use the posts/view.html and elements/post.html templates from the mobile folder.

However, because we did not specify a posts/list.html in the mobile theme, Ferris will use the posts/list.html in the root theme. In short, if a theme didn’t provide a template, it will fall back and use the root theme’s template.

You can set the theme from a controller using self.meta.view.theme:

def startup(self):
    self.meta.view.theme = 'mobile'

Functions, Filters, and Context

Ferris adds a few useful items to the template context.

Note

Although Sphinx displays these functions as belonging to the ‘template’ module, do not use that name when calling the function. Call the function using it’s name only.

Ferris Specific Utilities

template.format_value(value)

Uses format_value() to translate a value into a formatted string.

template.localize(datetime)

Maps to time_util.localize() to localize a datetime object.

template.json(obj)

Uses ferris.json_util to serialize an object to JSON. Can also be used as a filter.

template.inflector

Allows you to inflect words:

{{inflector.titleize('a_string')}}
{{inflector.underscore('SomeThing')}}
{{inflector.pluralize('plate')}}
{{inflector.singularize('plates')}}
template.ndb

Maps to google.appengine.ext.ndb.

The ferris object provides:

ferris.current_route_name

The name of the current route.

ferris.hostname

The current application’s hostname. This is from google.appengine.api.identity.get_default_version_hostname().

ferris.version

Ferris’ version string.

ferris.users

Maps to google.appengine.api.users.

ferris.is_current_user_admin()

Checks if the current user is an administrator as defined in the Google App Engine console. Maps directly to google.appengine.api.users.is_current_user_admin.

ferris.theme

The view’s current theme.

ferris.settings

All of the Settings configured for the application.

ferris.has_plugin(plugin)

Returns true if the given plugin is registered.

ferris.plugins

List of active plugins.

When rendering from a controller, the this object is available and provides:

this.name

The name of the current controller.

this.route

The current route - exposes Controller.route

This is also avaiable via the top-level alias route.

this.route.prefix

The route’s prefix

this.route.action

The route’s action

this.uri()

Maps to Controller.uri to generate urls.

This is also avaiable via the top-level alias uri.

this.uri_exists()

Maps to Controller.uri_exists to check the existance of routes.

This is also avaiable via the top-level alias uri_exists.

this.on_uri()

Maps to Controller.on_uri to check the the user is on the given route.

This is also avaiable via the top-level alias on_uri.

this.request

Exposes the webapp2.Request object.

This is also avaiable via the top-level alias request.

this.user()

The current user.

This is also avaiable via the top-level alias user.

this.encode_key()

Encodes a ndb key into an urlsafe string.

this.decode_key()

Decodes a urlsafe string into an ndb key.

General Utilities

Most of these map 1:1 to their python equivalents.

template.isinstance()
template.int()
template.float()
template.list()
template.str()
template.dir()

Value Formatting

ferris.core.template.format_value()[source]

Can be used to transform objects into strings.

Formatters are provided for datetime, date, ndb.Key, and ndb.Model classes. By default the date objects are localized and nicely formatted. The ndb formatters call the __unicode__ or __str__ method for their associated entities.

You can register additional formatters or overload the existing ones. Simply include something similar to the following in one of the bootstrap scripts (routes.py or listeners.py):

def format_foo(foo):
    return "%s: %s" % (foo.name, foo.value)

from ferris.core import template

template.formatters[Foo] = format_foo

Events

Similar to controller events, templates may also emit events by way of views. See Views for more information.

To trigger an event from a template use this.events like so:

{{this.events.my_event()}}

The default layouts that come with ferris offer the following built-in events:

layout_scripts

Fires just before the closing body tag.

layout_head

Fires just before the closing head tag.

layout_before_content

Fires just before the view’s content.

layout_after_content

Fires just after the view’s content.

Manual Rendering

Sometimes it’s useful to manually render a template outside of a controller or view context.

ferris.core.template.render_template(name, context=None, theme=None)[source]

Renders the template given by name with the given context (variables). Uses the global context.

Note that none of the context provided by the controller (this) will be available.

Example:

from ferris.core.template import render_template

context = {"test": "One"}
result = render_template("test/test_template.html", context=context)
print result