1: Template Layout Preparation¶
Get a Twitter Bootstrap-themed set of Jinja2 templates in place.
Background¶
In this traversal tutorial, we'll have a number of views and templates, each with some styling and layout. Let's work efficiently and produce decent visual appeal by getting some views and Jinja2 templates with our basic layout.
Objectives¶
- Get a basic Pyramid project in place with views and templates based on
pyramid_jinja2
. - Have a "layout" master template and some included subtemplates.
Steps¶
Let's start with an empty hierarchy of directories. Starting in a tutorial workspace (e.g.,
quick_traversal
):$ mkdir -p layout/tutorial/templates $ cd layout
Make a
layout/setup.py
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from setuptools import setup requires = [ 'pyramid', 'pyramid_jinja2', 'pyramid_debugtoolbar' ] setup(name='tutorial', install_requires=requires, entry_points="""\ [paste.app_factory] main = tutorial:main """, )
You can now install the project in development mode:
$ $VENV/bin/python setup.py develop
We need a configuration file at
layout/development.ini
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
[app:main] use = egg:tutorial pyramid.reload_templates = true pyramid.includes = pyramid_debugtoolbar [server:main] use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, tutorial [logger_tutorial] level = DEBUG handlers = qualname = tutorial [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration
In
layout/tutorial/__init__.py
wire uppyramid_jinja2
and scan for views:1 2 3 4 5 6 7 8
from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_jinja2') config.scan('.views') return config.make_wsgi_app()
Our views in
layout/tutorial/views.py
just has a single view that will answer an incoming request for/hello
:1 2 3 4 5 6 7 8 9 10 11
from pyramid.view import view_config class TutorialViews(object): def __init__(self, request): self.request = request @view_config(name='hello', renderer='templates/site.jinja2') def site(self): page_title = 'Quick Tutorial: Site View' return dict(page_title=page_title)
The view's renderer points to a template at
layout/tutorial/templates/site.jinja2
:1 2 3 4 5 6
{% extends "templates/layout.jinja2" %} {% block content %} <p>Welcome to the site.</p> {% endblock content %}
That template asks to use a master "layout" template at
layout/tutorial/templates/layout.jinja2
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
<!DOCTYPE html> <html lang="en"> <head> <title>{{ page_title }}</title> <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> </head> <body> <div class="navbar navbar-inverse"> <div class="container"> {% include "templates/header.jinja2" %} </div> </div> <div class="container"> <div> {% include "templates/breadcrumbs.jinja2" %} </div> <h1>{{ page_title }}</h1> {% block content %} {% endblock content %} </div> </body> </html>
The layout includes a header at
layout/tutorial/templates/header.jinja2
:1 2
<a class="navbar-brand" href="{{ request.resource_url(request.root) }}">Tutorial</a>
The layout also includes a subtemplate for breadcrumbs at
layout/tutorial/templates/breadcrumbs.jinja2
:1 2 3
<span> <a href="#">Home</a> >> </span>
Simplified tests in
layout/tutorial/tests.py
:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
import unittest from pyramid.testing import DummyRequest class TutorialViewsUnitTests(unittest.TestCase): def _makeOne(self, request): from .views import TutorialViews inst = TutorialViews(request) return inst def test_site_view(self): request = DummyRequest() inst = self._makeOne(request) result = inst.site() self.assertIn('Site View', result['page_title']) class TutorialFunctionalTests(unittest.TestCase): def setUp(self): from tutorial import main app = main({}) from webtest import TestApp self.testapp = TestApp(app) def test_it(self): result = self.testapp.get('/hello', status=200) self.assertIn(b'Site View', result.body)
Now run the tests:
$ $VENV/bin/nosetests tutorial . ---------------------------------------------------------------------- Ran 2 tests in 0.141s OK
Run your Pyramid application with:
$ $VENV/bin/pserve development.ini --reload
Open
http://localhost:6543/hello
in your browser.
Analysis¶
The @view_config
uses a new attribute: name='hello'
. This, as we'll see
in this traversal tutorial, makes a hello
location available in URLs.
The view's renderer uses Jinja2's mechanism for pointing at a master layout and filling certain areas from the view templates. The layout provides a basic HTML layout and points at Twitter Bootstrap CSS on a content delivery network for styling.