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:

     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
          """,
    )
    
  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
     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
    
  5. In layout/tutorial/__init__.py wire up pyramid_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()
    
  6. 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)
    
  7. 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 %}
    
  8. 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>
    
  9. 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>
    
  10. The layout also includes a subtemplate for breadcrumbs at layout/tutorial/templates/breadcrumbs.jinja2:

    1
    2
    3
    <span>
      <a href="#">Home</a> >> 
    </span>
    
  11. 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)
    
  12. Now run the tests:

    $ $VENV/bin/nosetests tutorial
    .
    ----------------------------------------------------------------------
    Ran 2 tests in 0.141s
    
    OK
    
  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.