Repository URL to install this package:
|
Version:
1.10.dev0 ▾
|
.. _qtut_more_view_classes:
==========================
15: More With View Classes
==========================
Group views into a class, sharing configuration, state, and logic.
Background
==========
As part of its mission to help build more ambitious web applications, Pyramid
provides many more features for views and view classes.
The Pyramid documentation discusses views as a Python "callable". This callable
can be a function, an object with a ``__call__``, or a Python class. In this
last case, methods on the class can be decorated with ``@view_config`` to
register the class methods with the :term:`configurator` as a view.
At first, our views were simple, free-standing functions. Many times your views
are related: different ways to look at or work on the same data, or a REST API
that handles multiple operations. Grouping these together as a :ref:`view class
<class_as_view>` makes sense:
- Group views.
- Centralize some repetitive defaults.
- Share some state and helpers.
Pyramid views have :ref:`view predicates <view_configuration_parameters>` that
determine which view is matched to a request, based on factors such as the
request method, the form parameters, and so on. These predicates provide many
axes of flexibility.
The following shows a simple example with four operations: view a home page
which leads to a form, save a change, and press the delete button.
Objectives
==========
- Group related views into a view class.
- Centralize configuration with class-level ``@view_defaults``.
- Dispatch one route/URL to multiple views based on request data.
- Share states and logic between views and templates via the view class.
Steps
=====
#. First we copy the results of the previous step:
.. code-block:: bash
$ cd ..; cp -r templating more_view_classes; cd more_view_classes
$ $VENV/bin/pip install -e .
#. Our route in ``more_view_classes/tutorial/__init__.py`` needs some
replacement patterns:
.. literalinclude:: more_view_classes/tutorial/__init__.py
:linenos:
#. Our ``more_view_classes/tutorial/views.py`` now has a view class with
several views:
.. literalinclude:: more_view_classes/tutorial/views.py
:linenos:
#. Our primary view needs a template at ``more_view_classes/tutorial/home.pt``:
.. literalinclude:: more_view_classes/tutorial/home.pt
:language: html
#. Ditto for our other view from the previous section at
``more_view_classes/tutorial/hello.pt``:
.. literalinclude:: more_view_classes/tutorial/hello.pt
:language: html
#. We have an edit view that also needs a template at
``more_view_classes/tutorial/edit.pt``:
.. literalinclude:: more_view_classes/tutorial/edit.pt
:language: html
#. And finally the delete view's template at
``more_view_classes/tutorial/delete.pt``:
.. literalinclude:: more_view_classes/tutorial/delete.pt
:language: html
#. Our tests in ``more_view_classes/tutorial/tests.py`` fail, so let's modify
them:
.. literalinclude:: more_view_classes/tutorial/tests.py
:linenos:
#. Now run the tests:
.. code-block:: bash
$ $VENV/bin/py.test tutorial/tests.py -q
..
2 passed in 0.40 seconds
#. Run your Pyramid application with:
.. code-block:: bash
$ $VENV/bin/pserve development.ini --reload
#. Open http://localhost:6543/howdy/jane/doe in your browser. Click the
``Save`` and ``Delete`` buttons, and watch the output in the console window.
Analysis
========
As you can see, the four views are logically grouped together. Specifically:
- We have a ``home`` view available at http://localhost:6543/ with a clickable
link to the ``hello`` view.
- The second view is returned when you go to ``/howdy/jane/doe``. This URL is
mapped to the ``hello`` route that we centrally set using the optional
``@view_defaults``.
- The third view is returned when the form is submitted with a ``POST`` method.
This rule is specified in the ``@view_config`` for that view.
- The fourth view is returned when clicking on a button such as ``<input
type="submit" name="form.delete" value="Delete"/>``.
In this step we show, using the following information as criteria, how to
decide which view to use:
- Method of the HTTP request (``GET``, ``POST``, etc.)
- Parameter information in the request (submitted form field names)
We also centralize part of the view configuration to the class level with
``@view_defaults``, then in one view, override that default just for that one
view. Finally, we put this commonality between views to work in the view class
by sharing:
- State assigned in ``TutorialViews.__init__``
- A computed value
These are then available both in the view methods and in the templates (e.g.,
``${view.view_name}`` and ``${view.full_name}``).
As a note, we made a switch in our templates on how we generate URLs. We
previously hardcoded the URLs, such as:
.. code-block:: html
<a href="/howdy/jane/doe">Howdy</a>
In ``home.pt`` we switched to:
.. code-block:: xml
<a href="${request.route_url('hello', first='jane',
last='doe')}">form</a>
Pyramid has rich facilities to help generate URLs in a flexible, non-error
prone fashion.
Extra credit
============
#. Why could our template do ``${view.full_name}`` and not have to do
``${view.full_name()}``?
#. The ``edit`` and ``delete`` views are both receive ``POST`` requests. Why
does the ``edit`` view configuration not catch the ``POST`` used by
``delete``?
#. We used Python ``@property`` on ``full_name``. If we reference this many
times in a template or view code, it would re-compute this every time. Does
Pyramid provide something that will cache the initial computation on a
property?
#. Can you associate more than one route with the same view?
#. There is also a ``request.route_path`` API. How does this differ from
``request.route_url``?
.. seealso:: :ref:`class_as_view`, `Weird Stuff You Can Do With
URL Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_