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

  1. 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
    
  2. Make a layout/setup.py:

     1from setuptools import setup
     2
     3requires = [
     4    'pyramid',
     5    'pyramid_jinja2',
     6    'pyramid_debugtoolbar'
     7]
     8
     9setup(name='tutorial',
    10      install_requires=requires,
    11      entry_points="""\
    12      [paste.app_factory]
    13      main = tutorial:main
    14      """,
    15)
    
  3. You can now install the project in development mode:

    $ $VENV/bin/python setup.py develop
    
  4. We need a configuration file at layout/development.ini:

     1[app:main]
     2use = egg:tutorial
     3pyramid.reload_templates = true
     4pyramid.includes =
     5    pyramid_debugtoolbar
     6
     7[server:main]
     8use = egg:pyramid#wsgiref
     9host = 0.0.0.0
    10port = 6543
    11
    12# Begin logging configuration
    13
    14[loggers]
    15keys = root, tutorial
    16
    17[logger_tutorial]
    18level = DEBUG
    19handlers =
    20qualname = tutorial
    21
    22[handlers]
    23keys = console
    24
    25[formatters]
    26keys = generic
    27
    28[logger_root]
    29level = INFO
    30handlers = console
    31
    32[handler_console]
    33class = StreamHandler
    34args = (sys.stderr,)
    35level = NOTSET
    36formatter = generic
    37
    38[formatter_generic]
    39format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
    40
    41# End logging configuration
    
  5. In layout/tutorial/__init__.py wire up pyramid_jinja2 and scan for views:

    1from pyramid.config import Configurator
    2
    3
    4def main(global_config, **settings):
    5    config = Configurator(settings=settings)
    6    config.include('pyramid_jinja2')
    7    config.scan('.views')
    8    return config.make_wsgi_app()
    
  6. Our views in layout/tutorial/views.py just has a single view that will answer an incoming request for /hello:

     1from pyramid.view import view_config
     2
     3
     4class TutorialViews(object):
     5    def __init__(self, request):
     6        self.request = request
     7
     8    @view_config(name='hello', renderer='templates/site.jinja2')
     9    def site(self):
    10        page_title = 'Quick Tutorial: Site View'
    11        return dict(page_title=page_title)
    
  7. The view's renderer points to a template at layout/tutorial/templates/site.jinja2:

    1{% extends "templates/layout.jinja2" %}
    2{% block content %}
    3
    4<p>Welcome to the site.</p>
    5
    6{% endblock content %}
    
  8. That template asks to use a master "layout" template at layout/tutorial/templates/layout.jinja2:

     1<!DOCTYPE html>
     2<html lang="en">
     3<head>
     4    <title>{{ page_title }}</title>
     5    <link rel="stylesheet"
     6          href="http://netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
     7</head>
     8<body>
     9
    10<div class="navbar navbar-inverse">
    11    <div class="container">
    12        {% include "templates/header.jinja2" %}
    13    </div>
    14</div>
    15
    16<div class="container">
    17
    18    <div>
    19        {% include "templates/breadcrumbs.jinja2" %}
    20    </div>
    21
    22    <h1>{{ page_title }}</h1>
    23    {% block content %}
    24    {% endblock content %}
    25
    26</div>
    27
    28</body>
    29</html>
    
  9. The layout includes a header at layout/tutorial/templates/header.jinja2:

    1<a class="navbar-brand" 
    2   href="{{ request.resource_url(request.root) }}">Tutorial</a>
    
  10. The layout also includes a subtemplate for breadcrumbs at layout/tutorial/templates/breadcrumbs.jinja2:

    1<span>
    2  <a href="#">Home</a> >> 
    3</span>
    
  11. Simplified tests in layout/tutorial/tests.py:

     1import unittest
     2
     3from pyramid.testing import DummyRequest
     4
     5
     6class TutorialViewsUnitTests(unittest.TestCase):
     7    def _makeOne(self, request):
     8        from .views import TutorialViews
     9        inst = TutorialViews(request)
    10        return inst
    11
    12    def test_site_view(self):
    13        request = DummyRequest()
    14        inst = self._makeOne(request)
    15        result = inst.site()
    16        self.assertIn('Site View', result['page_title'])
    17
    18
    19class TutorialFunctionalTests(unittest.TestCase):
    20    def setUp(self):
    21        from tutorial import main
    22        app = main({})
    23        from webtest import TestApp
    24        self.testapp = TestApp(app)
    25
    26    def test_it(self):
    27        result = self.testapp.get('/hello', status=200)
    28        self.assertIn(b'Site View', result.body)
    
  12. Now run the tests:

    1$ $VENV/bin/nosetests tutorial
    2.
    3----------------------------------------------------------------------
    4Ran 2 tests in 0.141s
    5
    6OK
    
  13. Run your Pyramid application with:

    $ $VENV/bin/pserve development.ini --reload
    
  14. 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.