Demo App With Pyramid Layout ============================ Let's see Pyramid Layout in action with the demo application provided in ``demo``. Installation ------------ Normal Pyramid stuff: #. Make a virtualenv #. ``env/bin/python demo/setup.py develop`` #. ``env/bin/pserve demo/development.ini`` #. Open ``http://0.0.0.0:6543/`` in a browser #. Click on the ``Home Mako``, ``Home Chameleon``, and ``Home Jinja2`` links in the header to see views for that use each. Now let's look at some of the code. Registration ------------ Pyramid Layout defines configuration directives and decorators you can use in your project. We need those loaded into our code. The demo does this in the ``etc/development.ini`` file: .. literalinclude:: ../demo/development.ini :lines: 9-12 :language: ini The ``development.ini`` entry point starts in ``demo/__init__.py``: .. literalinclude:: ../demo/demo/__init__.py :language: python :linenos: This is all Configurator action. We register a route for each view. We then scan our ``demo/layouts.py``, ``demo/panels.py``, and ``demo/views.py`` for registrations. Layout ------ Let's start with the big picture: the global look-and-feel via a :term:`layout`: .. literalinclude:: ../demo/demo/layouts.py :language: python :linenos: The ``@layout_config`` decorator comes from Pyramid Layout and allows us to define and register a :term:`layout`. In this case we've stacked 3 decorators, thus making 3 layouts, one for each template language. .. note:: The first ``@layout_config`` doesn't have a ``name`` and is thus the layout that you will get if your view doesn't specifically choose which layout it wants. Lines 21-24 illustrates the concept of keeping templates and the template logic close together. All views need to show the ``project_title``. It's part of the global look-and-feel :term:`main template`. So we put this logic on the *layout*, in one place as part of the global contract, rather than having each view supply that data/logic. Let's next look at where this is used in the template for one of the 3 layouts. In this case, the Mako template at ``demo/templates/layouts/layout.mako``: .. code-block:: mako ${layout.project_title}, from Pylons Project Here we see an important concept and some important magic: the template has a top-level variable ``layout`` available. This is an instance of your :term:`layout class`. For the ZPT crowd, if you look at the master template in ``demo/templates/layouts/layout.pt``, you might notice something weird at the top: there's no ``metal:define-macro``. Since Chameleon allows a template to be a top-level macro, Pyramid Layout automatically binds the entire template to the macro named ``main_template``. How does your view know to use a :term:`layout`? Let's take a look. Connecting Views to a Layout ---------------------------- Our demo app has a very simple set of views: .. literalinclude:: ../demo/demo/views.py :language: python :linenos: We again have one callable with 3 stacked decorators. The decorators are all normal Pyramid ``@view_config`` stuff. The second one points at a Chameleon template in ``demo/templates/home.pt``: .. literalinclude:: ../demo/demo/templates/home.pt :language: html The first line is the one that opts the template into the :term:`layout`. In ``home.jinja2`` that line looks like: .. code-block:: jinja {% extends main_template %} For both of these, ``main_template`` is inserted by Pyramid Layout, via a Pyramid renderer global, into the template's global namespace. After that, it's normal semantics for that template language. Back to ``views.py``. The view function grabs the ``Layout Manager``, which Pyramid Layout conveniently stashes on the request. The ``LayoutManager``'s primary job is getting/setting the current :term:`layout`. Which, of course, we do in this function. Our function then grabs the :term:`layout instance` and manipulates some state that is needed in the global look and feel. This, of course, could have been done in our ``AppLayout`` class, but in some cases, different views have different values for the headings. Re-Usable Snippets with Panels ------------------------------ Our :term:`main template` has something interesting in it: .. literalinclude:: ../demo/demo/templates/layouts/layout.mako :lines: 27-49 :emphasize-lines: 3,12 :language: mako Here we break our global layout into reusable parts via :term:`panels `. Where do these come from? ``@panel_config`` decorators, as shown in ``panels.py``. For example, this: .. code-block:: mako ${panel('navbar')} ...comes from this: .. literalinclude:: ../demo/demo/panels.py :language: python :lines: 4-25 The ``@panel_config`` registered a panel under the name ``navbar``, which our template could then use **or override**. The ``home.mako`` view template has a more interesting panel: .. code-block:: mako ${panel('hero', title='Mako')} ...which calls: .. literalinclude:: ../demo/demo/panels.py :language: python :lines: 28-33 :linenos: This shows that a :term:`panel` can be parameterized and used in different places in different ways.