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:
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:
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.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¶
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