summaryrefslogtreecommitdiff
path: root/docs/narr/handlers.rst
blob: 5b6f71984cea13b9f974a48e00f6a317648463bc (plain)
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
.. _handlers_chapter:

View Handlers
=============

Along with normal view callables, :app:`Pyramid` provides the concept of a
:term:`view handler`.  Using a view handler instead of a plain :term:`view
callable` makes it unnecessary to call
:meth:`pyramid.config.Configurator.add_route` (and/or
:meth:`pyramid.config.Configurator.add_view`) "by hand" multiple
times, making it more pleasant to register a collection of views as a single
class when using :term:`url dispatch`.  The view handler machinery also
introduces the concept of an ``action``, which is used as a :term:`view
predicate` to control which method of the handler is called.

.. note:: 

   View handlers are not useful when using :term:`traversal`, only when using
   :term:`url dispatch`.  The concept of a view handler is analogous to a
   "controller" in Pylons 1.0.

The view handler class is initialized by :app:`Pyramid` in the same
manner as a view class.  Its ``__init__`` is called with a request
object (see :ref:`class_as_view`) as its argument when a request enters
the system which corresponds with a view handler registration made
during configuration.  After the view handler class is instantiated, a
method on the instance is called. The method which is called depends on
the view handler configuration.

The :meth:`pyramid.config.Configurator.add_handler` method will
scan the handler class and automatically set up views for methods that
are auto-exposed, or were decorated with the
:class:`~pyramid.view.action` decorator. This decorator is used to setup
additional view configuration information for individual methods of the
class, and can be used repeatedly for a single view method to
register multiple view configurations for it.

Here's an example view handler class:

.. code-block:: python
    :linenos:
    
    from pyramid.response import Response
   
    from pyramid.view import action
   
    class Hello(object):
        def __init__(self, request):
            self.request = request
       
        def index(self):
            return Response('Hello world!')

        @action(renderer="mytemplate.mak")
        def bye(self):
            return {}

An accompanying call to the
:meth:`~pyramid.config.Configurator.add_handler` for the handler must
be performed in order to register it with the system:

.. code-block:: python
    :linenos:

    config.add_handler('hello', '/hello/{action}', handler=Hello)

This example will result in a route being added for the pattern
``/hello/{action}``, each method of the ``Hello`` class will then be examined
to register the views. The value of ``{action}`` in the route pattern will be
used to determine which view should be called, and each view in the class will
be setup with a view predicate that requires a specific ``action`` name.

If the URL in the above example was ``/hello/index``, then the ``index``
method of the Hello class would be called.

Alternatively, the action can be declared specifically for a URL to go to a
specific ``action`` name:

.. code-block:: python
    :linenos:
    
    config.add_handler('hello_index', '/hello/index', 
                       handler=Hello, action='index')

This will result one of the methods that are configured for the ``action`` of
'index' in the ``Hello`` handler class to be called. In this case the name
of the method is the same as the action name: 'index'. However, this
need not be the case, as we will see below.

.. note::

  Handler configuration may also be added to the system via :term:`ZCML` (see
  :ref:`zcml_handler_configuration`).

.. _using_add_handler:

Using :meth:`~pyramid.config.Configurator.add_handler`
-------------------------------------------------------------

When calling :meth:`~pyramid.config.Configurator.add_handler`, an
``action`` is required in either the route pattern or as a keyword argument,
but **cannot appear in both places**. A ``handler`` argument must also be
supplied, which can be either a :term:`asset specification` or a Python
reference to the handler class. Additional keyword arguments are passed
directly through to :meth:`pyramid.config.Configurator.add_route`.

For example:

.. code-block:: python
    :linenos:
    
    config.add_handler('hello', '/hello/{action}',
                       handler='mypackage.handlers:MyHandler')

In larger applications, it is advised to use a :term:`asset specification`
with :meth:`~pyramid.config.Configurator.add_handler` to avoid having
to import every handler class.

Multiple :meth:`~pyramid.config.Configurator.add_handler` calls can
specify the same handler, to register specific route names for different
handler/action combinations. For example:

.. code-block:: python
    :linenos:
    
    config.add_handler('hello_index', '/hello/index', 
                       handler=Hello, action='index')
    config.add_handler('bye_index', '/hello/bye', 
                       handler=Hello, action='bye')


View Setup in the Handler Class
-------------------------------

The handler class specified can have a single class level attribute called
``__autoexpose__`` which should be a regular expression or the value
``None``. It's used to determine which method names will result in additional
view configurations being registered.

When :meth:`~pyramid.config.Configurator.add_handler` runs, every
method in the handler class will be searched and a view registered if the
method name matches the ``__autoexpose__`` regular expression, or if the
method was decorated with :class:`~pyramid.view.action`.

Auto-exposed Views
------------------

Every method in the handler class that has a name meeting the
``_autoexpose__`` regular expression will have a view registered for an
``action`` name corresponding to the method name. This functionality can be
disabled by setting the ``__autoexpose__`` attribute to ``None``:

.. code-block:: python
    :linenos:

    from pyramid.view import action
   
    class Hello(object):
        __autoexpose__ = None
        
        def __init__(self, request):
            self.request = request
        
        @action()
        def index(self):
            return Response('Hello world!')

        @action(renderer="mytemplate.mak")
        def bye(self):
            return {}

With auto-expose effectively disabled, no views will be registered for a
method unless it is specifically decorated with :class:`~pyramid.view.action`.

Action Decorator
----------------

The :class:`~pyramid.view.action` decorator registers view configuration
information on the handler method, which is used by
:meth:`~pyramid.config.Configurator.add_handler` to setup the view
configuration.

All keyword arguments are recorded, and passed to
:meth:`~pyramid.config.Configurator.add_view`. Any valid keyword
arguments for :meth:`~pyramid.config.Configurator.add_view` can thus be
used with the :class:`~pyramid.view.action` decorator to further restrict when
the view will be called.

One important difference is that a handler method can respond to an ``action``
name that is different from the method name by passing in a ``name`` argument.

Example:

.. code-block:: python
    :linenos:
    
    from pyramid.view import action
   
    class Hello(object):
        def __init__(self, request):
            self.request = request
        
        @action(name='index', renderer='created.mak', request_method='POST')
        def create(self):
            return {}

        @action(renderer="view_all.mak", request_method='GET')
        def index(self):
            return {}

This will register two views that require the ``action`` to be ``index``, with
the additional view predicate requiring a specific request method.

It can be useful to decorate a single method multiple times with
:class:`~pyramid.view.action`. Each action decorator will register a new view
for the method. By specifying different names and renderers for each action,
the same view logic can be exposed and rendered differently on multiple
URLs.

Example:

.. code-block:: python
    :linenos:
    
    from pyramid.view import action
   
    class Hello(object):
        def __init__(self, request):
            self.request = request
        
        @action(name='home', renderer='home.mak')
        @action(name='about', renderer='about.mak')
        def show_template(self):
            # prep some template vars
            return {}

    # in the config
    config.add_handler('hello', '/hello/{action}', handler=Hello)

With this configuration, the url ``/hello/home`` will find a view configuration
that results in calling the ``show_template`` method, then rendering the
template with ``home.mak``, and the url ``/hello/about`` will call the same
method and render the ``about.mak`` template.