1.. _qtut_more_view_classes: 2 3========================== 415: More With View Classes 5========================== 6 7Group views into a class, sharing configuration, state, and logic. 8 9 10Background 11========== 12 13As part of its mission to help build more ambitious web applications, Pyramid 14provides many more features for views and view classes. 15 16The Pyramid documentation discusses views as a Python "callable". This callable 17can be a function, an object with a ``__call__``, or a Python class. In this 18last case, methods on the class can be decorated with ``@view_config`` to 19register the class methods with the :term:`configurator` as a view. 20 21At first, our views were simple, free-standing functions. Many times your views 22are related: different ways to look at or work on the same data, or a REST API 23that handles multiple operations. Grouping these together as a :ref:`view class 24<class_as_view>` makes sense: 25 26- Group views. 27 28- Centralize some repetitive defaults. 29 30- Share some state and helpers. 31 32Pyramid views have :ref:`view predicates <view_configuration_parameters>` that 33determine which view is matched to a request, based on factors such as the 34request method, the form parameters, and so on. These predicates provide many 35axes of flexibility. 36 37The following shows a simple example with four operations: view a home page 38which leads to a form, save a change, and press the delete button. 39 40 41Objectives 42========== 43 44- Group related views into a view class. 45 46- Centralize configuration with class-level ``@view_defaults``. 47 48- Dispatch one route/URL to multiple views based on request data. 49 50- Share states and logic between views and templates via the view class. 51 52 53Steps 54===== 55 56#. First we copy the results of the previous step: 57 58 .. code-block:: bash 59 60 $ cd ..; cp -r templating more_view_classes; cd more_view_classes 61 $ $VENV/bin/pip install -e . 62 63#. Our route in ``more_view_classes/tutorial/__init__.py`` needs some 64 replacement patterns: 65 66 .. literalinclude:: more_view_classes/tutorial/__init__.py 67 :linenos: 68 69#. Our ``more_view_classes/tutorial/views.py`` now has a view class with 70 several views: 71 72 .. literalinclude:: more_view_classes/tutorial/views.py 73 :linenos: 74 75#. Our primary view needs a template at ``more_view_classes/tutorial/home.pt``: 76 77 .. literalinclude:: more_view_classes/tutorial/home.pt 78 :language: html 79 80#. Ditto for our other view from the previous section at 81 ``more_view_classes/tutorial/hello.pt``: 82 83 .. literalinclude:: more_view_classes/tutorial/hello.pt 84 :language: html 85 86#. We have an edit view that also needs a template at 87 ``more_view_classes/tutorial/edit.pt``: 88 89 .. literalinclude:: more_view_classes/tutorial/edit.pt 90 :language: html 91 92#. And finally the delete view's template at 93 ``more_view_classes/tutorial/delete.pt``: 94 95 .. literalinclude:: more_view_classes/tutorial/delete.pt 96 :language: html 97 98#. Our tests in ``more_view_classes/tutorial/tests.py`` fail, so let's modify 99 them: 100 101 .. literalinclude:: more_view_classes/tutorial/tests.py 102 :linenos: 103 104#. Now run the tests: 105 106 .. code-block:: bash 107 108 $ $VENV/bin/py.test tutorial/tests.py -q 109 .. 110 2 passed in 0.40 seconds 111 112#. Run your Pyramid application with: 113 114 .. code-block:: bash 115 116 $ $VENV/bin/pserve development.ini --reload 117 118#. Open http://localhost:6543/howdy/jane/doe in your browser. Click the 119 ``Save`` and ``Delete`` buttons, and watch the output in the console window. 120 121 122Analysis 123======== 124 125As you can see, the four views are logically grouped together. Specifically: 126 127- We have a ``home`` view available at http://localhost:6543/ with a clickable 128 link to the ``hello`` view. 129 130- The second view is returned when you go to ``/howdy/jane/doe``. This URL is 131 mapped to the ``hello`` route that we centrally set using the optional 132 ``@view_defaults``. 133 134- The third view is returned when the form is submitted with a ``POST`` method. 135 This rule is specified in the ``@view_config`` for that view. 136 137- The fourth view is returned when clicking on a button such as ``<input 138 type="submit" name="form.delete" value="Delete"/>``. 139 140In this step we show, using the following information as criteria, how to 141decide which view to use: 142 143- Method of the HTTP request (``GET``, ``POST``, etc.) 144 145- Parameter information in the request (submitted form field names) 146 147We also centralize part of the view configuration to the class level with 148``@view_defaults``, then in one view, override that default just for that one 149view. Finally, we put this commonality between views to work in the view class 150by sharing: 151 152- State assigned in ``TutorialViews.__init__`` 153 154- A computed value 155 156These are then available both in the view methods and in the templates (e.g., 157``${view.view_name}`` and ``${view.full_name}``). 158 159As a note, we made a switch in our templates on how we generate URLs. We 160previously hardcoded the URLs, such as: 161 162.. code-block:: html 163 164 <a href="/howdy/jane/doe">Howdy</a> 165 166In ``home.pt`` we switched to: 167 168.. code-block:: xml 169 170 <a href="${request.route_url('hello', first='jane', 171 last='doe')}">form</a> 172 173Pyramid has rich facilities to help generate URLs in a flexible, non-error 174prone fashion. 175 176Extra credit 177============ 178 179#. Why could our template do ``${view.full_name}`` and not have to do 180 ``${view.full_name()}``? 181 182#. The ``edit`` and ``delete`` views are both receive ``POST`` requests. Why 183 does the ``edit`` view configuration not catch the ``POST`` used by 184 ``delete``? 185 186#. We used Python ``@property`` on ``full_name``. If we reference this many 187 times in a template or view code, it would re-compute this every time. Does 188 Pyramid provide something that will cache the initial computation on a 189 property? 190 191#. Can you associate more than one route with the same view? 192 193#. There is also a ``request.route_path`` API. How does this differ from 194 ``request.route_url``? 195 196.. seealso:: :ref:`class_as_view`, `Weird Stuff You Can Do With 197 URL Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_ 198