6: Templating

Web development usually involves HTML, and web frameworks generally provide support for HTML generation using templating languages.

Pyramid provides bundled support for the Chameleon and Mako templating engines, with active community support for bindings such as Jinja2.

Objectives

  • Create a template using Chameleon and associate it with a view
  • Change our tests to reflect how we test when views don’t render HTML directly

Steps

  1. Again, let’s use the previous package as a starting point for a new distribution. Also, make a directory for the templates:

    (env33)$ cd ../..; cp -r step05 step06; cd step06
    (env33)$ python3.3 setup.py develop
    (env33)$ mkdir tutorial/templates
    
  2. Our tutorial/views.py is now data-centric:

    1
    2
    3
    4
    5
    from pyramid.view import view_config
    
    @view_config(route_name='hello', renderer='templates/wiki_view.pt')
    def hello_world(request):
        return dict(title='Hello World')
    
  3. Create a Chameleon template in tutorial/templates/wiki_view.pt:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    <html>
    <head>
        <title>${title}</title>
    </head>
    <body>
    <div>
        <h1>${title}</h1>
    </div>
    </body>
    </html>
    
  4. Our tutorial/tests.py has a unit test which is also now data-centric:

     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
    import unittest
    
    from pyramid import testing
    
    
    class ViewTests(unittest.TestCase):
        def setUp(self):
            self.config = testing.setUp()
    
        def tearDown(self):
            testing.tearDown()
    
        def test_my_view(self):
            from tutorial.views import hello_world
            request = testing.DummyRequest()
            response = hello_world(request)
            self.assertEqual(response['title'], 'Hello World')
    
    
    class FunctionalTests(unittest.TestCase):
        def setUp(self):
            from tutorial import main
            settings = {}
            app = main(settings)
            from webtest import TestApp
            self.testapp = TestApp(app)
    
        def test_it(self):
            res = self.testapp.get('/', status=200)
            self.assertIn(b'Hello', res.body)
    
  5. Run the tests in your package using nose:

    (env33)$ nosetests .
    ..
    -----------------------------------------------------------------
    Ran 2 tests in 1.971s
    
    OK
    
  6. Run the WSGI application. Note the --reload:

    (env33)$ pserve development.ini --reload
    
  7. Open http://127.0.0.1:6547/ in your browser.

  8. Edit the template and reload your browser to see the changes. Do the same on the title returned in views.py.

Analysis

Our view function changed significantly. It is now a callable that returns data, which makes test-writing much more meaningful. Our @view_config wraps the view with a renderer that is pointed at a Chameleon template. Pyramid knows from the file suffix .pt that this should use the Chameleon engine for rendering the response.

The use of a templates directory is purely a matter of taste. You don’t have to have a magically-named directory, or any subdirectory at all.

Each kind of renderer (Chameleon, Mako, JSON, add-on renderers such as Jinja) manage what goes into the namespace of the template. Chameleon provides request automatically, for example, as well as the data returned from the view.

The --reload argument to pserve makes it watch for changes to certain kinds of files. For example, if you change a .py file, the application will restart automatically.

Extra Credit

  1. Will the application restart if I change my development.ini configuration file? Give it a try.
  2. What if I wanted to use Mako? Jinja2? Some brand new templating language?
  3. There was a little bit of lag as I visited some views for the first time. What do you think was happening?
  4. Can I write and register my own renderers? Should I? Can I share the renderers with other people?

Table Of Contents