.. _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.