CHANGES.txt | ●●●●● patch | view | raw | blame | history | |
docs/api/view.rst | ●●●●● patch | view | raw | blame | history | |
docs/narr/viewconfig.rst | ●●●●● patch | view | raw | blame | history | |
docs/whatsnew-1.3.rst | ●●●●● patch | view | raw | blame | history | |
pyramid/config/views.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_view.py | ●●●●● patch | view | raw | blame | history | |
pyramid/view.py | ●●●●● patch | view | raw | blame | history |
CHANGES.txt
@@ -1,3 +1,23 @@ Next release ============ Features -------- - New API: ``pyramid.view.view_defaults``. If you use a class as a view, you can use the new ``view_defaults`` class decorator on the class to provide defaults to the view configuration information used by every ``@view_config`` decorator that decorates a method of that class. It also works against view configurations involving a class made imperatively. Documentation ------------- - Added documentation to "View Configuration" narrative documentation chapter about ``view_defaults`` class decorator. - Added API docs for ``view_defaults`` class decorator. 1.3a1 (2011-12-09) ================== docs/api/view.rst
@@ -16,6 +16,9 @@ .. autoclass:: view_config :members: .. autoclass:: view_defaults :members: .. autoclass:: static :members: :inherited-members: docs/narr/viewconfig.rst
@@ -621,6 +621,7 @@ def amethod(self): return Response('hello') .. index:: single: add_view @@ -658,6 +659,186 @@ configuration to take effect. .. index:: single: view_defaults class decorator .. _view_defaults: ``@view_defaults`` Class Decorator ---------------------------------- .. note:: This feature is new in Pyramid 1.3. If you use a class as a view, you can use the :class:`pyramid.view.view_defaults` class decorator on the class to provide defaults to the view configuration information used by every ``@view_config`` decorator that decorates a method of that class. For instance, if you've got a class that has methods that represent "REST actions", all which are mapped to the same route, but different request methods, instead of this: .. code-block:: python :linenos: from pyramid.view import view_config from pyramid.response import Response class RESTView(object): def __init__(self, request): self.request = request @view_config(route_name='rest', request_method='GET') def get(self): return Response('get') @view_config(route_name='rest', request_method='POST') def post(self): return Response('post') @view_config(route_name='rest', request_method='DELETE') def delete(self): return Response('delete') You can do this: .. code-block:: python :linenos: from pyramid.view import view_defaults from pyramid.view import view_config from pyramid.response import Response @view_defaults(route_name='rest') class RESTView(object): def __init__(self, request): self.request = request @view_config(request_method='GET') def get(self): return Response('get') @view_config(request_method='POST') def post(self): return Response('post') @view_config(request_method='DELETE') def delete(self): return Response('delete') In the above example, we were able to take the ``route_name='rest'`` argument out of the call to each individual ``@view_config`` statement, because we used a ``@view_defaults`` class decorator to provide the argument as a default to each view method it possessed. Arguments passed to ``@view_config`` will override any default passed to ``@view_defaults``. The ``view_defaults`` class decorator can also provide defaults to the :meth:`pyramid.config.Configurator.add_view` directive when a decorated class is passed to that directive as its ``view`` argument. For example, instead of this: .. code-block:: python :linenos: from pyramid.response import Response from pyramid.config import Configurator class RESTView(object): def __init__(self, request): self.request = request def get(self): return Response('get') def post(self): return Response('post') def delete(self): return Response('delete') if __name__ == '__main__': config = Configurator() config.add_route('rest', '/rest') config.add_view( RESTView, route_name='rest', attr='get', request_method='GET') config.add_view( RESTView, route_name='rest', attr='post', request_method='POST') config.add_view( RESTView, route_name='rest', attr='delete', request_method='DELETE') To reduce the amount of repetion in the ``config.add_view`` statements, we can move the ``route_name='rest'`` argument to a ``@view_default`` class decorator on the RESTView class: .. code-block:: python :linenos: from pyramid.view import view_config from pyramid.response import Response from pyramid.config import Configurator @view_defaults(route_name='rest') class RESTView(object): def __init__(self, request): self.request = request def get(self): return Response('get') def post(self): return Response('post') def delete(self): return Response('delete') if __name__ == '__main__': config = Configurator() config.add_route('rest', '/rest') config.add_view(RESTView, attr='get', request_method='GET') config.add_view(RESTView, attr='post', request_method='POST') config.add_view(RESTView, attr='delete', request_method='DELETE') :class:`pyramid.view.view_defaults` accepts the same set of arguments that :class:`pyramid.view.view_config` does, and they have the same meaning. Each argument passed to ``view_defaults`` provides a default for the view configurations of methods of the class it's decorating. Normal Python inheritance rules apply to defaults added via ``view_defaults``. For example: .. code-block:: python :linenos: @view_defaults(route_name='rest') class Foo(object): pass class Bar(Foo): pass The ``Bar`` class above will inherit its view defaults from the arguments passed to the ``view_defaults`` decorator of the ``Foo`` class. To prevent this from happening, use a ``view_defaults`` decorator without any arguments on the subclass: .. code-block:: python :linenos: @view_defaults(route_name='rest') class Foo(object): pass @view_defaults() class Bar(Foo): pass The ``view_defaults`` decorator only works as a class decorator; using it against a function or a method will produce nonsensical results. .. index:: single: view security pair: security; view docs/whatsnew-1.3.rst
@@ -126,6 +126,70 @@ :attr:`pyramid.config.Configurator.introspectable`, :attr:`pyramid.registry.Registry.introspector`. ``@view_defaults`` Decorator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you use a class as a view, you can use the new :class:`pyramid.view.view_defaults` class decorator on the class to provide defaults to the view configuration information used by every ``@view_config`` decorator that decorates a method of that class. For instance, if you've got a class that has methods that represent "REST actions", all which are mapped to the same route, but different request methods, instead of this: .. code-block:: python :linenos: from pyramid.view import view_config from pyramid.response import Response class RESTView(object): def __init__(self, request): self.request = request @view_config(route_name='rest', request_method='GET') def get(self): return Response('get') @view_config(route_name='rest', request_method='POST') def post(self): return Response('post') @view_config(route_name='rest', request_method='DELETE') def delete(self): return Response('delete') You can do this: .. code-block:: python :linenos: from pyramid.view import view_defaults from pyramid.view import view_config from pyramid.response import Response @view_defaults(route_name='rest') class RESTView(object): def __init__(self, request): self.request = request @view_config(request_method='GET') def get(self): return Response('get') @view_config(request_method='POST') def post(self): return Response('post') @view_config(request_method='DELETE') def delete(self): return Response('delete') This also works for imperative view configurations that involve a class. See :ref:`view_defaults` for more information. Minor Feature Additions ----------------------- pyramid/config/views.py
@@ -562,7 +562,7 @@ if inspect.isclass(view): defaults = getattr(view, '__view_defaults__', {}).copy() defaults.update(kw) defaults['_backframes'] = 3 defaults['_backframes'] = 3 # for action_method return wrapped(*arg, **defaults) return wraps(wrapped)(wrapper) pyramid/tests/test_view.py
@@ -578,7 +578,7 @@ self.assertEqual(Foo.__view_defaults__['route_name'],'abc') self.assertEqual(Foo.__view_defaults__['renderer'],'def') def test_it_single_inheritance_non_overridden(self): def test_it_inheritance_not_overridden(self): from pyramid.view import view_defaults @view_defaults(route_name='abc', renderer='def') class Foo(object): pass @@ -586,6 +586,24 @@ self.assertEqual(Bar.__view_defaults__['route_name'],'abc') self.assertEqual(Bar.__view_defaults__['renderer'],'def') def test_it_inheritance_overriden(self): from pyramid.view import view_defaults @view_defaults(route_name='abc', renderer='def') class Foo(object): pass @view_defaults(route_name='ghi') class Bar(Foo): pass self.assertEqual(Bar.__view_defaults__['route_name'],'ghi') self.assertEqual(Bar.__view_defaults__['renderer'], None) def test_it_inheritance_overriden_empty(self): from pyramid.view import view_defaults @view_defaults(route_name='abc', renderer='def') class Foo(object): pass @view_defaults() class Bar(Foo): pass self.assertEqual(Bar.__view_defaults__['route_name'], None) self.assertEqual(Bar.__view_defaults__['renderer'], None) class ExceptionResponse(Exception): status = '404 Not Found' app_iter = ['Not Found'] pyramid/view.py
@@ -224,6 +224,14 @@ bfg_view = view_config # bw compat (forever) class view_defaults(view_config): """ A class :term:`decorator` which, when applied to a class, will provide defaults for all view configurations that use the class. This decorator accepts all the arguments accepted by :class:`pyramid.config.view_config`, and each has the same meaning. See :ref:`view_defaults` for more information. """ def __call__(self, wrapped): wrapped.__view_defaults__ = self.__dict__.copy() return wrapped