summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorcguardia <carlos.delaguardia@gmail.com>2011-04-25 00:37:33 -0500
committercguardia <carlos.delaguardia@gmail.com>2011-04-25 00:37:33 -0500
commitb61680ad87abcb524ae3fe846cd591c6ec1d54ef (patch)
treee2fcd960d9119efde4a1b7eecec3880f98397d2f /docs
parent1b395e6d43b9450ea652d9addb675936b507ea52 (diff)
parentbb825910b3b68b4b167572fd5252cc99be88e17e (diff)
downloadpyramid-b61680ad87abcb524ae3fe846cd591c6ec1d54ef.tar.gz
pyramid-b61680ad87abcb524ae3fe846cd591c6ec1d54ef.tar.bz2
pyramid-b61680ad87abcb524ae3fe846cd591c6ec1d54ef.zip
Merge branch 'master' of https://github.com/Pylons/pyramid
Diffstat (limited to 'docs')
-rw-r--r--docs/api/request.rst45
-rw-r--r--docs/conf.py2
-rw-r--r--docs/narr/advconfig.rst12
-rw-r--r--docs/narr/assets.rst7
-rw-r--r--docs/narr/environment.rst63
-rw-r--r--docs/narr/hybrid.rst59
-rw-r--r--docs/narr/renderers.rst74
-rw-r--r--docs/narr/templates.rst14
-rw-r--r--docs/narr/urldispatch.rst152
-rw-r--r--docs/narr/viewconfig.rst3
-rw-r--r--docs/narr/zca.rst22
-rw-r--r--docs/tutorials/wiki/authorization.rst218
-rw-r--r--docs/tutorials/wiki/definingmodels.rst70
-rw-r--r--docs/tutorials/wiki/definingviews.rst45
-rw-r--r--docs/tutorials/wiki/index.rst1
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/tests.py216
-rw-r--r--docs/tutorials/wiki/tests.rst78
-rw-r--r--docs/tutorials/wiki2/authorization.rst28
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst32
-rw-r--r--docs/tutorials/wiki2/definingmodels.rst8
-rw-r--r--docs/tutorials/wiki2/definingviews.rst38
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py39
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py5
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py5
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py21
25 files changed, 812 insertions, 445 deletions
diff --git a/docs/api/request.rst b/docs/api/request.rst
index d17441c0a..639d0fcd8 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -85,6 +85,41 @@
of ``request.exception`` will be ``None`` within response and
finished callbacks.
+ .. attribute:: response
+
+ This attribute is actually a "reified" property which returns an
+ instance of the :class:`pyramid.response.Response` class. The response
+ object returned does not exist until this attribute is accessed. Once
+ it is accessed, subsequent accesses to this request object will return
+ the same :class:`~pyramid.response.Response` object.
+
+ The ``request.response`` API can is used by renderers. A render obtains
+ the response object it will return from a view that uses that renderer
+ by accessing ``request.response``. Therefore, it's possible to use the
+ ``request.response`` API to set up a response object with "the right"
+ attributes (e.g. by calling ``request.response.set_cookie(...)`` or
+ ``request.response.content_type = 'text/plain'``, etc) within a view
+ that uses a renderer. For example, within a view that uses a
+ :term:`renderer`::
+
+ response = request.response
+ response.set_cookie('mycookie', 'mine, all mine!')
+ return {'text':'Value that will be used by the renderer'}
+
+ Mutations to this response object will be preserved in the response sent
+ to the client after rendering.
+
+ Non-renderer code can also make use of request.response instead of
+ creating a response "by hand". For example, in view code::
+
+ response = request.response
+ response.body = 'Hello!'
+ response.content_type = 'text/plain'
+ return response
+
+ Note that the response in this circumstance is not "global"; it still
+ must be returned from the view code if a renderer is not used.
+
.. attribute:: session
If a :term:`session factory` has been configured, this attribute
@@ -127,10 +162,18 @@
.. attribute:: response_*
+ .. warning:: As of Pyramid 1.1, assignment to ``response_*`` attrs are
+ deprecated. Assigning to one will cause a deprecation warning to be
+ emitted. Instead of assigning ``response_*`` attributes to the
+ request, use API of the the :attr:`pyramid.request.Request.response`
+ object (exposed to view code as ``request.response``) to influence
+ response behavior.
+
You can set attributes on a :class:`pyramid.request.Request` which will
influence the behavor of *rendered* responses (views which use a
:term:`renderer` and which don't directly return a response). These
attributes begin with ``response_``, such as ``response_headerlist``. If
you need to influence response values from a view that uses a renderer
(such as the status code, a header, the content type, etc) see,
- :ref:`response_request_attrs`.
+ :ref:`response_prefixed_attrs`.
+
diff --git a/docs/conf.py b/docs/conf.py
index a987106d4..a610351ff 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -93,7 +93,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.0'
+version = '1.1a0'
# The full version, including alpha/beta/rc tags.
release = version
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
index 099bce35f..5ee554284 100644
--- a/docs/narr/advconfig.rst
+++ b/docs/narr/advconfig.rst
@@ -295,15 +295,9 @@ These are the methods of the configurator which provide conflict detection:
:meth:`~pyramid.config.Configurator.set_locale_negotiator` and
:meth:`~pyramid.config.Configurator.set_default_permission`.
-Some other methods of the configurator also indirectly provide conflict
-detection, because they're implemented in terms of conflict-aware methods:
-
-- :meth:`~pyramid.config.Configurator.add_route` does a second type of
- conflict detection when a ``view`` parameter is passed (it calls
- ``add_view``).
-
-- :meth:`~pyramid.config.Configurator.static_view`, a frontend for
- ``add_route`` and ``add_view``.
+:meth:`~pyramid.config.Configurator.add_static_view` also indirectly
+provides conflict detection, because it's implemented in terms of the
+conflict-aware ``add_route`` and ``add_view`` methods.
.. _including_configuration:
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index bbb673ecc..8d0e7058c 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -341,7 +341,8 @@ application's startup code.
# .. every other add_route declaration should come
# before this one, as it will, by default, catch all requests
- config.add_route('catchall_static', '/*subpath', 'myapp.static.static_view')
+ config.add_route('catchall_static', '/*subpath')
+ config.add_view('myapp.static.static_view', route_name='catchall_static')
The special name ``*subpath`` above is used by the
:class:`~pyramid.view.static` view callable to signify the path of the file
@@ -384,8 +385,8 @@ Or you might register it to be the view callable for a particular route:
.. code-block:: python
:linenos:
- config.add_route('favicon', '/favicon.ico',
- view='myapp.views.favicon_view')
+ config.add_route('favicon', '/favicon.ico')
+ config.add_view('myapp.views.favicon_view', route_name='favicon')
Because this is a simple view callable, it can be protected with a
:term:`permission` or can be configured to respond under different
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index 7b7946aae..8c299f3a3 100644
--- a/docs/narr/environment.rst
+++ b/docs/narr/environment.rst
@@ -381,3 +381,66 @@ around in overridden asset directories. ``reload_assets`` makes the system
*very slow* when templates are in use. Never set ``reload_assets`` to
``True`` on a production system.
+Adding A Custom Setting
+-----------------------
+
+From time to time, you may need to add a custom setting to your application.
+Here's how:
+
+- If you're using an ``.ini`` file, change the ``.ini`` file, adding the
+ setting to the ``[app:foo]`` section representing your Pyramid application.
+ For example:
+
+ .. code-block:: ini
+
+ [app:myapp]
+ # .. other settings
+ debug_frobnosticator = True
+
+- In the ``main()`` function that represents the place that your Pyramid WSGI
+ application is created, anticipate that you'll be getting this key/value
+ pair as a setting and do any type conversion necessary.
+
+ If you've done any type conversion of your custom value, reset the
+ converted values into the ``settings`` dictionary *before* you pass the
+ dictionary as ``settings`` to the :term:`Configurator`. For example:
+
+ .. code-block:: python
+
+ def main(global_config, **settings):
+ # ...
+ from pyramid.settings import asbool
+ debug_frobnosticator = asbool(settings.get(
+ 'debug_frobnosticator', 'false'))
+ settings['debug_frobnosticator'] = debug_frobnosticator
+ config = Configurator(settings=settings)
+
+ .. note:: It's especially important that you mutate the ``settings``
+ dictionary with the converted version of the variable *before* passing
+ it to the Configurator: the configurator makes a *copy* of ``settings``,
+ it doesn't use the one you pass directly.
+
+- In the runtime code that you need to access the new settings value, find
+ the value in the ``registry.settings`` dictionary and use it. In
+ :term:`view` code (or any other code that has access to the request), the
+ easiest way to do this is via ``request.registry.settings``. For example:
+
+ .. code-block:: python
+
+ registry = request.registry.settings
+ debug_frobnosticator = settings['debug_frobnosticator']
+
+ If you wish to use the value in code that does not have access to the
+ request and you wish to use the value, you'll need to use the
+ :func:`pyramid.threadlocal.get_current_registry` API to obtain the current
+ registry, then ask for its ``settings`` attribute. For example:
+
+ .. code-block:: python
+
+ registry = pyramid.threadlocal.get_current_registry()
+ settings = registry.settings
+ debug_frobnosticator = settings['debug_frobnosticator']
+
+
+
+
diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst
index 780cb0975..f8ed743fb 100644
--- a/docs/narr/hybrid.rst
+++ b/docs/narr/hybrid.rst
@@ -33,7 +33,7 @@ URL Dispatch Only
~~~~~~~~~~~~~~~~~
An application that uses :term:`url dispatch` exclusively to map URLs to code
-will often have statements like this within your application startup
+will often have statements like this within application startup
configuration:
.. code-block:: python
@@ -41,15 +41,20 @@ configuration:
# config is an instance of pyramid.config.Configurator
- config.add_route('foobar', '{foo}/{bar}', view='myproject.views.foobar')
- config.add_route('bazbuz', '{baz}/{buz}', view='myproject.views.bazbuz')
+ config.add_route('foobar', '{foo}/{bar}')
+ config.add_route('bazbuz', '{baz}/{buz}')
-Each :term:`route` typically corresponds to a single view callable,
-and when that route is matched during a request, the view callable
-named by the ``view`` attribute is invoked.
+ config.add_view('myproject.views.foobar', route_name='foobar')
+ config.add_view('myproject.views.bazbuz', route_name='bazbuz')
-Typically, an application that uses only URL dispatch won't perform any calls
-to :meth:`pyramid.config.Configurator.add_view` in its startup code.
+Each :term:`route` corresponds to one or more view callables. Each view
+callable is associated with a route by passing a ``route_name`` parameter
+that matches its name during a call to
+:meth:`~pyramid.config.Configurator.add_view`. When a route is matched
+during a request, :term:`view lookup` is used to match the request to its
+associated view callable. The presence of calls to
+:meth:`~pyramid.config.Configurator.add_route` signify that an application is
+using URL dispatch.
Traversal Only
~~~~~~~~~~~~~~
@@ -196,12 +201,9 @@ remainder becomes the path used to perform traversal.
The ``*remainder`` route pattern syntax is explained in more
detail within :ref:`route_pattern_syntax`.
-Note that unlike the examples provided within :ref:`urldispatch_chapter`, the
-``add_route`` configuration statement named previously does not pass a
-``view`` argument. This is because a hybrid mode application relies on
-:term:`traversal` to do :term:`resource location` and :term:`view lookup`
-instead of invariably invoking a specific view callable named directly within
-the matched route's configuration.
+A hybrid mode application relies more heavily on :term:`traversal` to do
+:term:`resource location` and :term:`view lookup` than most examples indicate
+within :ref:`urldispatch_chapter`.
Because the pattern of the above route ends with ``*traverse``, when this
route configuration is matched during a request, :app:`Pyramid` will attempt
@@ -426,13 +428,11 @@ attribute.
Using ``*subpath`` in a Route Pattern
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-There are certain extremely rare cases when you'd like to influence
-the traversal :term:`subpath` when a route matches without actually
-performing traversal. For instance, the
-:func:`pyramid.wsgi.wsgiapp2` decorator and the
-:class:`pyramid.view.static` helper attempt to compute
-``PATH_INFO`` from the request's subpath, so it's useful to be able to
-influence this value.
+There are certain extremely rare cases when you'd like to influence the
+traversal :term:`subpath` when a route matches without actually performing
+traversal. For instance, the :func:`pyramid.wsgi.wsgiapp2` decorator and the
+:class:`pyramid.view.static` helper attempt to compute ``PATH_INFO`` from the
+request's subpath, so it's useful to be able to influence this value.
When ``*subpath`` exists in a pattern, no path is actually traversed,
but the traversal algorithm will return a :term:`subpath` list implied
@@ -442,8 +442,8 @@ commonly in route declarations that look like this:
.. code-block:: python
:linenos:
- config.add_route('static', '/static/*subpath',
- view='mypackage.views.static_view')
+ config.add_route('static', '/static/*subpath')
+ config.add_view('mypackage.views.static_view', route_name='static')
Where ``mypackage.views.static_view`` is an instance of
:class:`pyramid.view.static`. This effectively tells the static helper to
@@ -458,11 +458,16 @@ application. We'll detail them here.
Registering a Default View for a Route That Has a ``view`` Attribute
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. warning:: As of :app:`Pyramid` 1.1 this section is slated to be removed in
+ a later documentation release because the the ability to add views
+ directly to the :term:`route configuration` by passing a ``view`` argument
+ to ``add_route`` has been deprecated.
+
It is an error to provide *both* a ``view`` argument to a :term:`route
configuration` *and* a :term:`view configuration` which names a
``route_name`` that has no ``name`` value or the empty ``name`` value. For
-example, this pair of declarations will generate a "conflict" error at
-startup time.
+example, this pair of declarations will generate a conflict error at startup
+time.
.. code-block:: python
:linenos:
@@ -490,8 +495,8 @@ Can also be spelled like so:
config.add_route('home', '{foo}/{bar}/*traverse')
config.add_view('myproject.views.home', route_name='home')
-The two spellings are logically equivalent. In fact, the former is
-just a syntactical shortcut for the latter.
+The two spellings are logically equivalent. In fact, the former is just a
+syntactical shortcut for the latter.
Binding Extra Views Against a Route Configuration that Doesn't Have a ``*traverse`` Element In Its Pattern
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index 0b7cdb834..c3533648b 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -92,8 +92,8 @@ will be employed.
return HTTPFound(location='http://example.com') # any renderer avoided
Views which use a renderer can vary non-body response attributes (such as
-headers and the HTTP status code) by attaching properties to the request.
-See :ref:`response_request_attrs`.
+headers and the HTTP status code) by attaching a property to the
+``request.response`` attribute See :ref:`request_response_attr`.
Additional renderers can be added by developers to the system as necessary
(see :ref:`adding_and_overriding_renderers`).
@@ -147,7 +147,8 @@ representing the ``str()`` serialization of the return value:
{'content': 'Hello!'}
Views which use the string renderer can vary non-body response attributes by
-attaching properties to the request. See :ref:`response_request_attrs`.
+using the API of the ``request.response`` attribute. See
+:ref:`request_response_attr`.
.. index::
pair: renderer; JSON
@@ -199,7 +200,8 @@ You can configure a view to use the JSON renderer by naming ``json`` as the
Views which use the JSON renderer can vary non-body response attributes by
-attaching properties to the request. See :ref:`response_request_attrs`.
+using the api of the ``request.response`` attribute. See
+:ref:`request_response_attr`.
.. index::
pair: renderer; chameleon
@@ -269,8 +271,9 @@ Here's an example view configuration which uses a Chameleon text renderer:
context='myproject.resources.Hello',
renderer='myproject:templates/foo.txt')
-Views which use a Chameleon renderer can vary response attributes by
-attaching properties to the request. See :ref:`response_request_attrs`.
+Views which use a Chameleon renderer can vary response attributes by using
+the API of the ``request.response`` attribute. See
+:ref:`request_response_attr`.
.. index::
pair: renderer; mako
@@ -333,7 +336,7 @@ additional :ref:`mako_template_renderer_settings`.
single: response headers (from a renderer)
single: renderer response headers
-.. _response_request_attrs:
+.. _request_response_attr:
Varying Attributes of Rendered Responses
----------------------------------------
@@ -342,9 +345,43 @@ Before a response constructed by a :term:`renderer` is returned to
:app:`Pyramid`, several attributes of the request are examined which have the
potential to influence response behavior.
-View callables that don't directly return a response should set these
-attributes on the ``request`` object via ``setattr`` during their execution,
-to influence associated response attributes.
+View callables that don't directly return a response should use the API of
+the :class:`pyramid.response.Response` attribute available as
+``request.response`` during their execution, to influence associated response
+behavior.
+
+For example, if you need to change the response status from within a view
+callable that uses a renderer, assign the ``status`` attribute to the
+``response`` attribute of the request before returning a result:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.view import view_config
+
+ @view_config(name='gone', renderer='templates/gone.pt')
+ def myview(request):
+ request.response.status = '404 Not Found'
+ return {'URL':request.URL}
+
+For more information on attributes of the request, see the API documentation
+in :ref:`request_module`. For more information on the API of
+``request.response``, see :class:`pyramid.response.Response`.
+
+.. _response_prefixed_attrs:
+
+Deprecated Mechanism to Vary Attributes of Rendered Responses
+-------------------------------------------------------------
+
+.. warning:: This section describes behavior deprecated in Pyramid 1.1.
+
+In previous releases of Pyramid (1.0 and before), the ``request.response``
+attribute did not exist. Instead, Pyramid required users to set special
+``response_`` -prefixed attributes of the request to influence response
+behavior. As of Pyramid 1.1, those request attributes are deprecated and
+their use will cause a deprecation warning to be issued when used. Until
+their existence is removed completely, we document them below, for benefit of
+people with older code bases.
``response_content_type``
Defines the content-type of the resulting response,
@@ -367,23 +404,6 @@ to influence associated response attributes.
returning various values in the ``response_headerlist``, this is purely a
convenience.
-For example, if you need to change the response status from within a view
-callable that uses a renderer, assign the ``response_status`` attribute to
-the request before returning a result:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
-
- @view_config(name='gone', renderer='templates/gone.pt')
- def myview(request):
- request.response_status = '404 Not Found'
- return {'URL':request.URL}
-
-For more information on attributes of the request, see the API
-documentation in :ref:`request_module`.
-
.. index::
single: renderer (adding)
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 426ec229b..150b173e3 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -367,13 +367,13 @@ templates as renderers. See :ref:`available_template_system_bindings`.
render a view without needing to fork your code to do so. See
:ref:`extending_chapter` for more information.
-By default, views rendered via a template renderer return a
-:term:`Response` object which has a *status code* of ``200 OK``, and a
-*content-type* of ``text/html``. To vary attributes of the response
-of a view that uses a renderer, such as the content-type, headers, or
-status attributes, you must set attributes on the *request* object
-within the view before returning the dictionary. See
-:ref:`response_request_attrs` for more information.
+By default, views rendered via a template renderer return a :term:`Response`
+object which has a *status code* of ``200 OK``, and a *content-type* of
+``text/html``. To vary attributes of the response of a view that uses a
+renderer, such as the content-type, headers, or status attributes, you must
+use the API of the :class:`pyramid.response.Response` object exposed as
+``request.response`` within the view before returning the dictionary. See
+:ref:`request_response_attr` for more information.
The same set of system values are provided to templates rendered via a
renderer view configuration as those provided to templates rendered
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 219753882..1024dd188 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -54,17 +54,19 @@ Route Configuration
-------------------
:term:`Route configuration` is the act of adding a new :term:`route` to an
-application. A route has a *pattern*, representing a pattern meant to match
+application. A route has a *name*, which acts as an identifier to be used
+for URL generation. The name also allows developers to associate a view
+configuration with the route. A route also has a *pattern*, meant to match
against the ``PATH_INFO`` portion of a URL (the portion following the scheme
-and port, e.g. ``/foo/bar`` in the URL ``http://localhost:8080/foo/bar``),
-and a *route name*, which is used by developers within a :app:`Pyramid`
-application to uniquely identify a particular route when generating a URL.
-It also optionally has a ``factory``, a set of :term:`route predicate`
-parameters, and a set of :term:`view` parameters.
+and port, e.g. ``/foo/bar`` in the URL ``http://localhost:8080/foo/bar``). It
+also optionally has a ``factory`` and a set of :term:`route predicate`
+attributes.
.. index::
single: add_route
+.. _config-add-route:
+
Configuring a Route via The ``add_route`` Configurator Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -79,7 +81,8 @@ example:
# pyramid.config.Configurator class; "myview" is assumed
# to be a "view callable" function
from views import myview
- config.add_route('myroute', '/prefix/{one}/{two}', view=myview)
+ config.add_route('myroute', '/prefix/{one}/{two}')
+ config.add_view(myview, route_name='myroute')
.. versionchanged:: 1.0a4
Prior to 1.0a4, routes allow for a marker starting with a ``:``, for
@@ -89,9 +92,47 @@ example:
.. index::
single: route configuration; view callable
+.. _add_route_view_config:
+
Route Configuration That Names a View Callable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.. warning:: This section describes a feature which has been deprecated in
+ Pyramid 1.1 and higher. In order to reduce confusion and documentation
+ burden, passing view-related parameters to
+ :meth:`~pyramid.config.Configurator.add_route` is deprecated.
+
+ In versions earlier than 1.1, a view was permitted to be connected to a
+ route using a set of ``view*`` parameters passed to the
+ :meth:`~pyramid.config.Configurator.add_route`. This was a shorthand
+ which replaced the need to perform a subsequent call to
+ :meth:`~pyramid.config.Configurator.add_view` as described in
+ :ref:`config-add-route`. For example, it was valid (and often recommended)
+ to do:
+
+ .. code-block:: python
+
+ config.add_route('home', '/', view='mypackage.views.myview',
+ view_renderer='some/renderer.pt')
+
+ Instead of the equivalent:
+
+ .. code-block:: python
+
+ config.add_route('home', '/')
+ config.add_view('mypackage.views.myview', route_name='home')
+ renderer='some/renderer.pt')
+
+ Passing ``view*`` arguments to ``add_route`` as shown in the first
+ example above is now deprecated in favor of connecting a view to a
+ predefined route via :meth:`~pyramid.config.Configurator.add_view` using
+ the route's ``route_name`` parameter, as shown in the second example
+ above.
+
+ A deprecation warning is now issued when any view-related parameter is
+ passed to ``Configurator.add_route``. The recommended way to associate a
+ view with a route is documented in :ref:`config-add-route`.
+
When a route configuration declaration names a ``view`` attribute, the value
of the attribute will reference a :term:`view callable`. This view callable
will be invoked when the route matches. A view callable, as described in
@@ -125,6 +166,9 @@ When a route configuration names a ``view`` attribute, the :term:`view
callable` named as that ``view`` attribute will always be found and invoked
when the associated route pattern matches during a request.
+See :meth:`pyramid.config.Configurator.add_route` for a description of
+view-related arguments.
+
.. index::
single: route path pattern syntax
@@ -363,8 +407,9 @@ resource of the view callable ultimately found via :term:`view lookup`.
.. code-block:: python
:linenos:
- config.add_route('abc', '/abc', view='myproject.views.theview',
+ config.add_route('abc', '/abc',
factory='myproject.resources.root_factory')
+ config.add_view('myproject.views.theview', route_name='abc')
The factory can either be a Python object or a :term:`dotted Python name` (a
string) which points to such a Python object, as it is above.
@@ -395,7 +440,8 @@ process. Examples of route predicate arguments are ``pattern``, ``xhr``, and
``request_method``.
Other arguments are view configuration related arguments. These only have an
-effect when the route configuration names a ``view``.
+effect when the route configuration names a ``view``. These arguments have
+been deprecated as of :app:`Pyramid` 1.1 (see :ref:`add_route_view_config`).
Other arguments are ``name`` and ``factory``. These arguments represent
neither predicates nor view configuration information.
@@ -547,8 +593,8 @@ If any route matches, the route matching process stops. The :term:`request`
is decorated with a special :term:`interface` which describes it as a "route
request", the :term:`context` resource is generated, and the context and the
resulting request are handed off to :term:`view lookup`. During view lookup,
-if any ``view`` argument was provided within the matched route configuration,
-the :term:`view callable` it points to is called.
+if a :term:`view callable` associated with the matched route is found, that
+view is called.
When a route configuration is declared, it may contain :term:`route
predicate` arguments. All route predicates associated with a route
@@ -621,7 +667,8 @@ result in a particular view callable being invoked:
.. code-block:: python
:linenos:
- config.add_route('idea', 'site/{id}', view='mypackage.views.site_view')
+ config.add_route('idea', 'site/{id}')
+ config.add_view('mypackage.views.site_view', route_name='idea')
When a route configuration with a ``view`` attribute is added to the system,
and an incoming request matches the *pattern* of the route configuration, the
@@ -665,9 +712,13 @@ add to your application:
.. code-block:: python
:linenos:
- config.add_route('idea', 'ideas/{idea}', view='mypackage.views.idea_view')
- config.add_route('user', 'users/{user}', view='mypackage.views.user_view')
- config.add_route('tag', 'tags/{tags}', view='mypackage.views.tag_view')
+ config.add_route('idea', 'ideas/{idea}')
+ config.add_route('user', 'users/{user}')
+ config.add_route('tag', 'tags/{tags}')
+
+ config.add_view('mypackage.views.idea_view', route_name='idea')
+ config.add_view('mypackage.views.user_view', route_name='user')
+ config.add_view('mypackage.views.tag_view', route_name='tag')
The above configuration will allow :app:`Pyramid` to service URLs in these
forms:
@@ -717,9 +768,8 @@ An example of using a route with a factory:
.. code-block:: python
:linenos:
- config.add_route('idea', 'ideas/{idea}',
- view='myproject.views.idea_view',
- factory='myproject.resources.Idea')
+ config.add_route('idea', 'ideas/{idea}', factory='myproject.resources.Idea')
+ config.add_view('myproject.views.idea_view', route_name='idea')
The above route will manufacture an ``Idea`` resource as a :term:`context`,
assuming that ``mypackage.resources.Idea`` resolves to a class that accepts a
@@ -735,34 +785,6 @@ request in its ``__init__``. For example:
In a more complicated application, this root factory might be a class
representing a :term:`SQLAlchemy` model.
-Example 4
-~~~~~~~~~
-
-It is possible to create a route declaration without a ``view`` attribute,
-but associate the route with a :term:`view callable` using a ``view``
-declaration.
-
-.. code-block:: python
- :linenos:
-
- config.add_route('idea', 'site/{id}')
- config.add_view(route_name='idea', view='mypackage.views.site_view')
-
-This set of configuration parameters creates a configuration completely
-equivalent to this example provided in :ref:`urldispatch_example1`:
-
-.. code-block:: python
- :linenos:
-
- config.add_route('idea', 'site/{id}', view='mypackage.views.site_view')
-
-In fact, the spelling which names a ``view`` attribute is just syntactic
-sugar for the more verbose spelling which contains separate view and route
-registrations.
-
-More uses for this style of associating views with routes are explored in
-:ref:`hybrid_chapter`.
-
.. index::
single: matching the root URL
single: root url (matching)
@@ -777,14 +799,14 @@ It's not entirely obvious how to use a route pattern to match the root URL
.. code-block:: python
:linenos:
- config.add_route('root', '', view='mypackage.views.root_view')
+ config.add_route('root', '')
Or provide the literal string ``/`` as the pattern:
.. code-block:: python
:linenos:
- config.add_route('root', '/', view='mypackage.views.root_view')
+ config.add_route('root', '/')
.. index::
single: generating route URLs
@@ -834,10 +856,11 @@ route configuration looks like so:
.. code-block:: python
:linenos:
- config.add_route('noslash', 'no_slash',
- view='myproject.views.no_slash')
- config.add_route('hasslash', 'has_slash/',
- view='myproject.views.has_slash')
+ config.add_route('noslash', 'no_slash')
+ config.add_route('hasslash', 'has_slash/')
+
+ config.add_view('myproject.views.no_slash', route_name='noslash')
+ config.add_view('myproject.views.has_slash', route_name='hasslash')
If a request enters the application with the ``PATH_INFO`` value of
``/has_slash/``, the second route will match. If a request enters the
@@ -864,8 +887,8 @@ the application's startup configuration, adding the following stanza:
.. code-block:: python
:linenos:
- config.add_view(context='pyramid.exceptions.NotFound',
- view='pyramid.view.append_slash_notfound_view')
+ config.add_view('pyramid.view.append_slash_notfound_view',
+ context='pyramid.exceptions.NotFound')
See :ref:`view_module` and :ref:`changing_the_notfound_view` for more
information about the slash-appending not found view and for a more general
@@ -1063,30 +1086,25 @@ is executed.
Route View Callable Registration and Lookup Details
---------------------------------------------------
-The purpose of making it possible to specify a view callable within a route
-configuration is to prevent developers from needing to deeply understand the
-details of :term:`resource location` and :term:`view lookup`. When a route
-names a view callable as a ``view`` argument, and a request enters the system
-which matches the pattern of the route, the result is simple: the view
-callable associated with the route is invoked with the request that caused
-the invocation.
+When a request enters the system which matches the pattern of the route, the
+usual result is simple: the view callable associated with the route is
+invoked with the request that caused the invocation.
For most usage, you needn't understand more than this; how it works is an
implementation detail. In the interest of completeness, however, we'll
explain how it *does* work in the this section. You can skip it if you're
uninterested.
-When a ``view`` attribute is attached to a route configuration,
-:app:`Pyramid` ensures that a :term:`view configuration` is registered that
-will always be found when the route pattern is matched during a request. To
-do so:
+When a view is associated with a route configuration, :app:`Pyramid` ensures
+that a :term:`view configuration` is registered that will always be found
+when the route pattern is matched during a request. To do so:
- A special route-specific :term:`interface` is created at startup time for
each route configuration declaration.
-- When a route configuration declaration mentions a ``view`` attribute, a
+- When an ``add_view`` statement mentions a ``route name`` attribute, a
:term:`view configuration` is registered at startup time. This view
- configuration uses the route-specific interface as a :term:`request` type.
+ configuration uses a route-specific interface as a :term:`request` type.
- At runtime, when a request causes any route to match, the :term:`request`
object is decorated with the route-specific interface.
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index 9b2500a2b..743cc016e 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -59,7 +59,8 @@ View configuration is performed in one of these ways:
- By specifying a view within a :term:`route configuration`. View
configuration via a route configuration is performed by using the
:meth:`pyramid.config.Configurator.add_route` method, passing a ``view``
- argument specifying a view callable.
+ argument specifying a view callable. This pattern of view configuration is
+ deprecated as of :app:`Pyramid` 1.1.
.. note:: A package named ``pyramid_handlers`` (available from PyPI) provides
an analogue of :term:`Pylons` -style "controllers", which are a special
diff --git a/docs/narr/zca.rst b/docs/narr/zca.rst
index fcab0653e..19c52d0c9 100644
--- a/docs/narr/zca.rst
+++ b/docs/narr/zca.rst
@@ -66,15 +66,15 @@ more than a single application per process. For example, use of a
:term:`Paste` "composite" allows you to run separate individual WSGI
applications in the same process, each answering requests for some URL
prefix. This makes it possible to run, for example, a TurboGears
-application at ``/turbogears`` and a BFG application at ``/bfg``, both
-served up using the same :term:`WSGI` server within a single Python
-process.
+application at ``/turbogears`` and a :app:`Pyramid` application at
+``/pyramid``, both served up using the same :term:`WSGI` server
+within a single Python process.
Most production Zope applications are relatively large, making it
impractical due to memory constraints to run more than one Zope
-application per Python process. However, a :app:`Pyramid`
-application may be very small and consume very little memory, so it's
-a reasonable goal to be able to run more than one BFG application per
+application per Python process. However, a :app:`Pyramid` application
+may be very small and consume very little memory, so it's a reasonable
+goal to be able to run more than one :app:`Pyramid` application per
process.
In order to make it possible to run more than one :app:`Pyramid`
@@ -182,10 +182,10 @@ global ZCA API. Without special treatment, the ZCA global APIs will
always return the global ZCA registry (the one in
``zope.component.globalregistry.base``).
-To "fix" this and make the ZCA global APIs use the "current" BFG
-registry, you need to call
-:meth:`~pyramid.config.Configurator.hook_zca` within your
-setup code. For example:
+To "fix" this and make the ZCA global APIs use the "current"
+:app:`Pyramid` registry, you need to call
+:meth:`~pyramid.config.Configurator.hook_zca` within your setup code.
+For example:
.. code-block:: python
:linenos:
@@ -253,7 +253,7 @@ Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves
the global ZCA component registry. Line 6 creates a
:term:`Configurator`, passing the global ZCA registry into its
constructor as the ``registry`` argument. Line 7 "sets up" the global
-registry with BFG-specific registrations; this is code that is
+registry with Pyramid-specific registrations; this is code that is
normally executed when a registry is constructed rather than created,
but we must call it "by hand" when we pass an explicit registry.
diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst
index ee86eb543..e4480d6d9 100644
--- a/docs/tutorials/wiki/authorization.rst
+++ b/docs/tutorials/wiki/authorization.rst
@@ -7,21 +7,25 @@ edit, and add pages to our wiki. For purposes of demonstration we'll change
our application to allow people whom are members of a *group* named
``group:editors`` to add and edit wiki pages but we'll continue allowing
anyone with access to the server to view pages. :app:`Pyramid` provides
-facilities for *authorization* and *authentication*. We'll make use of both
-features to provide security to our application.
+facilities for :term:`authorization` and :term:`authentication`. We'll make
+use of both features to provide security to our application.
-The source code for this tutorial stage can be browsed via
-`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/
-<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/>`_.
+We will add an :term:`authentication policy` and an
+:term:`authorization policy` to our :term:`application
+registry`, add a ``security.py`` module and give our :term:`root`
+resource an :term:`ACL`.
+Then we will add ``login`` and ``logout`` views, and modify the
+existing views to make them return a ``logged_in`` flag to the
+renderer and add :term:`permission` declarations to their ``view_config``
+decorators.
-Configuring a ``pyramid`` Authentication Policy
---------------------------------------------------
+Finally, we will add a ``login.pt`` template and change the existing
+``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in.
-For any :app:`Pyramid` application to perform authorization, we need to add a
-``security.py`` module and we'll need to change our :term:`application
-registry` to add an :term:`authentication policy` and a :term:`authorization
-policy`.
+The source code for this tutorial stage can be browsed via
+`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/
+<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/authorization/>`_.
Adding Authentication and Authorization Policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -64,6 +68,43 @@ user and groups sources. Note that the ``editor`` user is a member of the
``group:editors`` group in our dummy group data (the ``GROUPS`` data
structure).
+Giving Our Root Resource an ACL
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+We need to give our root resource object an :term:`ACL`. This ACL will be
+sufficient to provide enough information to the :app:`Pyramid` security
+machinery to challenge a user who doesn't have appropriate credentials when
+he attempts to invoke the ``add_page`` or ``edit_page`` views.
+
+We need to perform some imports at module scope in our ``models.py`` file:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.security import Allow
+ from pyramid.security import Everyone
+
+Our root resource object is a ``Wiki`` instance. We'll add the following
+line at class scope to our ``Wiki`` class:
+
+.. code-block:: python
+ :linenos:
+
+ __acl__ = [ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit') ]
+
+It's only happenstance that we're assigning this ACL at class scope. An ACL
+can be attached to an object *instance* too; this is how "row level security"
+can be achieved in :app:`Pyramid` applications. We actually only need *one*
+ACL for the entire system, however, because our security requirements are
+simple, so this feature is not demonstrated.
+
+Our resulting ``models.py`` file will now look like so:
+
+.. literalinclude:: src/authorization/tutorial/models.py
+ :linenos:
+ :language: python
+
Adding Login and Logout Views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -129,6 +170,38 @@ template. For example:
logged_in = logged_in,
edit_url = edit_url)
+Adding ``permission`` Declarations to our ``view_config`` Decorators
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To protect each of our views with a particular permission, we need to pass a
+``permission`` argument to each of our :class:`pyramid.view.view_config`
+decorators. To do so, within ``views.py``:
+
+- We add ``permission='view'`` to the decorator attached to the
+ ``view_wiki`` and ``view_page`` view functions. This makes the
+ assertion that only users who possess the ``view`` permission
+ against the context resource at the time of the request may
+ invoke these views. We've granted
+ :data:`pyramid.security.Everyone` the view permission at the
+ root model via its ACL, so everyone will be able to invoke the
+ ``view_wiki`` and ``view_page`` views.
+
+- We add ``permission='edit'`` to the decorator attached to the
+ ``add_page`` and ``edit_page`` view functions. This makes the
+ assertion that only users who possess the effective ``edit``
+ permission against the context resource at the time of the
+ request may invoke these views. We've granted the
+ ``group:editors`` principal the ``edit`` permission at the
+ root model via its ACL, so only a user whom is a member of
+ the group named ``group:editors`` will able to invoke the
+ ``add_page`` or ``edit_page`` views. We've likewise given
+ the ``editor`` user membership to this group via the
+ ``security.py`` file by mapping him to the ``group:editors``
+ group in the ``GROUPS`` data structure (``GROUPS
+ = {'editor':['group:editors']}``); the ``groupfinder``
+ function consults the ``GROUPS`` data structure. This means
+ that the ``editor`` user can add and edit pages.
+
Adding the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -154,92 +227,29 @@ class="app-welcome align-right">`` div:
<a href="${request.application_url}/logout">Logout</a>
</span>
-Giving Our Root Resource an ACL
--------------------------------
-
-We need to give our root resource object an :term:`ACL`. This ACL will be
-sufficient to provide enough information to the :app:`Pyramid` security
-machinery to challenge a user who doesn't have appropriate credentials when
-he attempts to invoke the ``add_page`` or ``edit_page`` views.
+Seeing Our Changes To ``views.py`` and our Templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-We need to perform some imports at module scope in our ``models.py`` file:
+Our ``views.py`` module will look something like this when we're done:
-.. code-block:: python
+.. literalinclude:: src/authorization/tutorial/views.py
:linenos:
+ :language: python
- from pyramid.security import Allow
- from pyramid.security import Everyone
-
-Our root resource object is a ``Wiki`` instance. We'll add the following
-line at class scope to our ``Wiki`` class:
+Our ``edit.pt`` template will look something like this when we're done:
-.. code-block:: python
+.. literalinclude:: src/authorization/tutorial/templates/edit.pt
:linenos:
+ :language: xml
- __acl__ = [ (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit') ]
-
-It's only happenstance that we're assigning this ACL at class scope. An ACL
-can be attached to an object *instance* too; this is how "row level security"
-can be achieved in :app:`Pyramid` applications. We actually only need *one*
-ACL for the entire system, however, because our security requirements are
-simple, so this feature is not demonstrated.
-
-Our resulting ``models.py`` file will now look like so:
+Our ``view.pt`` template will look something like this when we're done:
-.. literalinclude:: src/authorization/tutorial/models.py
+.. literalinclude:: src/authorization/tutorial/templates/view.pt
:linenos:
- :language: python
-
-Adding ``permission`` Declarations to our ``view_config`` Decorators
---------------------------------------------------------------------
-
-To protect each of our views with a particular permission, we need to pass a
-``permission`` argument to each of our :class:`pyramid.view.view_config`
-decorators. To do so, within ``views.py``:
-
-- We add ``permission='view'`` to the decorator attached to the ``view_wiki``
- view function. This makes the assertion that only users who possess the
- ``view`` permission against the context resource at the time of the request
- may invoke this view. We've granted :data:`pyramid.security.Everyone` the
- view permission at the root model via its ACL, so everyone will be able to
- invoke the ``view_wiki`` view.
-
-- We add ``permission='view'`` to the decorator attached to the ``view_page``
- view function. This makes the assertion that only users who possess the
- effective ``view`` permission against the context resource at the time of
- the request may invoke this view. We've granted
- :data:`pyramid.security.Everyone` the view permission at the root model via
- its ACL, so everyone will be able to invoke the ``view_page`` view.
-
-- We add ``permission='edit'`` to the decorator attached to the ``add_page``
- view function. This makes the assertion that only users who possess the
- effective ``edit`` permission against the context resource at the time of
- the request may invoke this view. We've granted the ``group:editors``
- principal the ``edit`` permission at the root model via its ACL, so only
- the a user whom is a member of the group named ``group:editors`` will able
- to invoke the ``add_page`` view. We've likewise given the ``editor`` user
- membership to this group via thes ``security.py`` file by mapping him to
- the ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
- {'editor':['group:editors']}``); the ``groupfinder`` function consults the
- ``GROUPS`` data structure. This means that the ``editor`` user can add
- pages.
-
-- We add ``permission='edit'`` to the decorator attached to the ``edit_page``
- view function. This makes the assertion that only users who possess the
- effective ``edit`` permission against the context resource at the time of
- the request may invoke this view. We've granted the ``group:editors``
- principal the ``edit`` permission at the root model via its ACL, so only
- the a user whom is a member of the group named ``group:editors`` will able
- to invoke the ``edit_page`` view. We've likewise given the ``editor`` user
- membership to this group via thes ``security.py`` file by mapping him to
- the ``group:editors`` group in the ``GROUPS`` data structure (``GROUPS =
- {'editor':['group:editors']}``); the ``groupfinder`` function consults the
- ``GROUPS`` data structure. This means that the ``editor`` user can edit
- pages.
+ :language: xml
Viewing the Application in a Browser
-------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We can finally examine our application in a browser. The views we'll try are
as follows:
@@ -267,35 +277,7 @@ as follows:
credentials with the username ``editor``, password ``editor`` will
show the edit page form being displayed.
-Seeing Our Changes To ``views.py`` and our Templates
-----------------------------------------------------
-
-Our ``views.py`` module will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/views.py
- :linenos:
- :language: python
-
-Our ``edit.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/edit.pt
- :linenos:
- :language: xml
-
-Our ``view.pt`` template will look something like this when we're done:
-
-.. literalinclude:: src/authorization/tutorial/templates/view.pt
- :linenos:
- :language: xml
-
-Revisiting the Application
----------------------------
-
-When we revisit the application in a browser, and log in (as a result
-of hitting an edit or add page and submitting the login form with the
-``editor`` credentials), we'll see a Logout link in the upper right
-hand corner. When we click it, we're logged out, and redirected back
-to the front page.
-
-
-
+- After logging in (as a result of hitting an edit or add page and
+ submitting the login form with the ``editor`` credentials), we'll see
+ a Logout link in the upper right hand corner. When we click it,
+ we're logged out, and redirected back to the front page.
diff --git a/docs/tutorials/wiki/definingmodels.rst b/docs/tutorials/wiki/definingmodels.rst
index 3d2d01061..baf497458 100644
--- a/docs/tutorials/wiki/definingmodels.rst
+++ b/docs/tutorials/wiki/definingmodels.rst
@@ -89,70 +89,16 @@ something like this:
:linenos:
:language: python
-Removing View Configuration
----------------------------
-
-In a previous step in this chapter, we removed the
-``tutorial.models.MyModel`` class. However, our ``views.py`` module still
-attempts to import this class. Temporarily, we'll change ``views.py`` so
-that it no longer references ``MyModel`` by removing its imports and removing
-a reference to it from the arguments passed to the ``@view_config``
-:term:`configuration decoration` decorator which sits atop the ``my_view``
-view callable.
-
-The result of all of our edits to ``views.py`` will end up looking
-something like this:
-
-.. literalinclude:: src/models/tutorial/views.py
- :linenos:
- :language: python
-
-Testing the Models
-------------------
-
-To make sure the code we just wrote works, we write tests for the model
-classes and the appmaker. Changing ``tests.py``, we'll write a separate test
-class for each model class, and we'll write a test class for the
-``appmaker``.
-
-To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided as a
-result of the ``pyramid_zodb`` project generator. We'll add three test
-classes: one for the ``Page`` model named ``PageModelTests``, one for the
-``Wiki`` model named ``WikiModelTests``, and one for the appmaker named
-``AppmakerTests``.
-
-When we're done changing ``tests.py``, it will look something like so:
-
-.. literalinclude:: src/models/tutorial/tests.py
- :linenos:
- :language: python
+Viewing the Application in a Browser
+------------------------------------
-Running the Tests
------------------
-
-We can run these tests by using ``setup.py test`` in the same way we
-did in :ref:`running_tests`. Assuming our shell's current working
-directory is the "tutorial" distribution directory:
-
-On UNIX:
-
-.. code-block:: text
-
- $ ../bin/python setup.py test -q
-
-On Windows:
-
-.. code-block:: text
-
- c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
-
-The expected output is something like this:
+We can't. At this point, our system is in a "non-runnable" state; we'll need
+to change view-related files in the next chapter to be able to start the
+application successfully. If you try to start the application, you'll wind
+up with a Python traceback on your console that ends with this exception:
.. code-block:: text
- .....
- ----------------------------------------------------------------------
- Ran 5 tests in 0.008s
-
- OK
+ ImportError: cannot import name MyModel
+This will also happen if you attempt to run the tests.
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index 233e571f1..b6c083bbf 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -318,48 +318,3 @@ browser. The views we'll try are as follows:
will generate an ``IndexError`` for the expression
``request.subpath[0]``. You'll see an interactive traceback
facility provided by :term:`WebError`.
-
-Testing the Views
-=================
-
-We'll modify our ``tests.py`` file, adding tests for each view function we
-added above. As a result, we'll *delete* the ``ViewTests`` test in the file,
-and add four other test classes: ``ViewWikiTests``, ``ViewPageTests``,
-``AddPageTests``, and ``EditPageTests``. These test the ``view_wiki``,
-``view_page``, ``add_page``, and ``edit_page`` views respectively.
-
-Once we're done with the ``tests.py`` module, it will look a lot like the
-below:
-
-.. literalinclude:: src/views/tutorial/tests.py
- :linenos:
- :language: python
-
-Running the Tests
-=================
-
-We can run these tests by using ``setup.py test`` in the same way we did in
-:ref:`running_tests`. Assuming our shell's current working directory is the
-"tutorial" distribution directory:
-
-On UNIX:
-
-.. code-block:: text
-
- $ ../bin/python setup.py test -q
-
-On Windows:
-
-.. code-block:: text
-
- c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
-
-The expected result looks something like:
-
-.. code-block:: text
-
- .........
- ----------------------------------------------------------------------
- Ran 9 tests in 0.203s
-
- OK
diff --git a/docs/tutorials/wiki/index.rst b/docs/tutorials/wiki/index.rst
index 660bf3bd3..c984c4f01 100644
--- a/docs/tutorials/wiki/index.rst
+++ b/docs/tutorials/wiki/index.rst
@@ -23,5 +23,6 @@ tutorial can be browsed at
definingmodels
definingviews
authorization
+ tests
distributing
diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py
new file mode 100644
index 000000000..d9ff866f1
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py
@@ -0,0 +1,216 @@
+import unittest
+
+from pyramid import testing
+
+class PageModelTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from tutorial.models import Page
+ return Page
+
+ def _makeOne(self, data=u'some data'):
+ return self._getTargetClass()(data=data)
+
+ def test_constructor(self):
+ instance = self._makeOne()
+ self.assertEqual(instance.data, u'some data')
+
+class WikiModelTests(unittest.TestCase):
+
+ def _getTargetClass(self):
+ from tutorial.models import Wiki
+ return Wiki
+
+ def _makeOne(self):
+ return self._getTargetClass()()
+
+ def test_it(self):
+ wiki = self._makeOne()
+ self.assertEqual(wiki.__parent__, None)
+ self.assertEqual(wiki.__name__, None)
+
+class AppmakerTests(unittest.TestCase):
+ def _callFUT(self, zodb_root):
+ from tutorial.models import appmaker
+ return appmaker(zodb_root)
+
+ def test_it(self):
+ root = {}
+ self._callFUT(root)
+ self.assertEqual(root['app_root']['FrontPage'].data,
+ 'This is the front page')
+
+class ViewWikiTests(unittest.TestCase):
+ def test_it(self):
+ from tutorial.views import view_wiki
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ response = view_wiki(context, request)
+ self.assertEqual(response.location, 'http://example.com/FrontPage')
+
+class ViewPageTests(unittest.TestCase):
+ def _callFUT(self, context, request):
+ from tutorial.views import view_page
+ return view_page(context, request)
+
+ def test_it(self):
+ wiki = testing.DummyResource()
+ wiki['IDoExist'] = testing.DummyResource()
+ context = testing.DummyResource(data='Hello CruelWorld IDoExist')
+ context.__parent__ = wiki
+ context.__name__ = 'thepage'
+ request = testing.DummyRequest()
+ info = self._callFUT(context, request)
+ self.assertEqual(info['page'], context)
+ self.assertEqual(
+ info['content'],
+ '<div class="document">\n'
+ '<p>Hello <a href="http://example.com/add_page/CruelWorld">'
+ 'CruelWorld</a> '
+ '<a href="http://example.com/IDoExist/">'
+ 'IDoExist</a>'
+ '</p>\n</div>\n')
+ self.assertEqual(info['edit_url'],
+ 'http://example.com/thepage/edit_page')
+
+
+class AddPageTests(unittest.TestCase):
+ def _callFUT(self, context, request):
+ from tutorial.views import add_page
+ return add_page(context, request)
+
+ def test_it_notsubmitted(self):
+ from pyramid.url import resource_url
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ request.subpath = ['AnotherPage']
+ info = self._callFUT(context, request)
+ self.assertEqual(info['page'].data,'')
+ self.assertEqual(
+ info['save_url'],
+ resource_url(context, request, 'add_page', 'AnotherPage'))
+
+ def test_it_submitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ request.subpath = ['AnotherPage']
+ self._callFUT(context, request)
+ page = context['AnotherPage']
+ self.assertEqual(page.data, 'Hello yo!')
+ self.assertEqual(page.__name__, 'AnotherPage')
+ self.assertEqual(page.__parent__, context)
+
+class EditPageTests(unittest.TestCase):
+ def _callFUT(self, context, request):
+ from tutorial.views import edit_page
+ return edit_page(context, request)
+
+ def test_it_notsubmitted(self):
+ from pyramid.url import resource_url
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ info = self._callFUT(context, request)
+ self.assertEqual(info['page'], context)
+ self.assertEqual(info['save_url'],
+ resource_url(context, request, 'edit_page'))
+
+ def test_it_submitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest({'form.submitted':True,
+ 'body':'Hello yo!'})
+ response = self._callFUT(context, request)
+ self.assertEqual(response.location, 'http://example.com/')
+ self.assertEqual(context.data, 'Hello yo!')
+
+class FunctionalTests(unittest.TestCase):
+
+ viewer_login = '/login?login=viewer&password=viewer' \
+ '&came_from=FrontPage&form.submitted=Login'
+ viewer_wrong_login = '/login?login=viewer&password=incorrect' \
+ '&came_from=FrontPage&form.submitted=Login'
+ editor_login = '/login?login=editor&password=editor' \
+ '&came_from=FrontPage&form.submitted=Login'
+
+ def setUp(self):
+ import tempfile
+ import os.path
+ from tutorial import main
+ self.tmpdir = tempfile.mkdtemp()
+
+ dbpath = os.path.join( self.tmpdir, 'test.db')
+ settings = { 'zodb_uri' : 'file://' + dbpath }
+
+ app = main({}, **settings)
+ from repoze.zodbconn.middleware import EnvironmentDeleterMiddleware
+ app = EnvironmentDeleterMiddleware(app)
+ from webtest import TestApp
+ self.testapp = TestApp(app)
+
+ def tearDown(self):
+ import shutil
+ shutil.rmtree( self.tmpdir )
+
+ def test_root(self):
+ res = self.testapp.get('/', status=302)
+ self.assertTrue(not res.body)
+
+ def test_FrontPage(self):
+ res = self.testapp.get('/FrontPage', status=200)
+ self.assertTrue('FrontPage' in res.body)
+
+ def test_unexisting_page(self):
+ res = self.testapp.get('/SomePage', status=404)
+ self.assertTrue('Not Found' in res.body)
+
+ def test_successful_log_in(self):
+ res = self.testapp.get( self.viewer_login, status=302)
+ self.assertTrue(res.location == 'FrontPage')
+
+ def test_failed_log_in(self):
+ res = self.testapp.get( self.viewer_wrong_login, status=200)
+ self.assertTrue('login' in res.body)
+
+ def test_logout_link_present_when_logged_in(self):
+ res = self.testapp.get( self.viewer_login, status=302)
+ res = self.testapp.get('/FrontPage', status=200)
+ self.assertTrue('Logout' in res.body)
+
+ def test_logout_link_not_present_after_logged_out(self):
+ res = self.testapp.get( self.viewer_login, status=302)
+ res = self.testapp.get('/FrontPage', status=200)
+ res = self.testapp.get('/logout', status=302)
+ self.assertTrue('Logout' not in res.body)
+
+ def test_anonymous_user_cannot_edit(self):
+ res = self.testapp.get('/FrontPage/edit_page', status=200)
+ self.assertTrue('Login' in res.body)
+
+ def test_anonymous_user_cannot_add(self):
+ res = self.testapp.get('/add_page/NewPage', status=200)
+ self.assertTrue('Login' in res.body)
+
+ def test_viewer_user_cannot_edit(self):
+ res = self.testapp.get( self.viewer_login, status=302)
+ res = self.testapp.get('/FrontPage/edit_page', status=200)
+ self.assertTrue('Login' in res.body)
+
+ def test_viewer_user_cannot_add(self):
+ res = self.testapp.get( self.viewer_login, status=302)
+ res = self.testapp.get('/add_page/NewPage', status=200)
+ self.assertTrue('Login' in res.body)
+
+ def test_editors_member_user_can_edit(self):
+ res = self.testapp.get( self.editor_login, status=302)
+ res = self.testapp.get('/FrontPage/edit_page', status=200)
+ self.assertTrue('Editing' in res.body)
+
+ def test_editors_member_user_can_add(self):
+ res = self.testapp.get( self.editor_login, status=302)
+ res = self.testapp.get('/add_page/NewPage', status=200)
+ self.assertTrue('Editing' in res.body)
+
+ def test_editors_member_user_can_view(self):
+ res = self.testapp.get( self.editor_login, status=302)
+ res = self.testapp.get('/FrontPage', status=200)
+ self.assertTrue('FrontPage' in res.body)
diff --git a/docs/tutorials/wiki/tests.rst b/docs/tutorials/wiki/tests.rst
new file mode 100644
index 000000000..f3151dbcc
--- /dev/null
+++ b/docs/tutorials/wiki/tests.rst
@@ -0,0 +1,78 @@
+============
+Adding Tests
+============
+
+We will now add tests for the models and the views and a few functional
+tests in the ``tests.py``. Tests ensure that an application works, and
+that it continues to work after some changes are made in the future.
+
+Testing the Models
+==================
+
+We write tests for the model
+classes and the appmaker. Changing ``tests.py``, we'll write a separate test
+class for each model class, and we'll write a test class for the
+``appmaker``.
+
+To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided as a
+result of the ``pyramid_zodb`` project generator. We'll add three test
+classes: one for the ``Page`` model named ``PageModelTests``, one for the
+``Wiki`` model named ``WikiModelTests``, and one for the appmaker named
+``AppmakerTests``.
+
+Testing the Views
+=================
+
+We'll modify our ``tests.py`` file, adding tests for each view function we
+added above. As a result, we'll *delete* the ``ViewTests`` test in the file,
+and add four other test classes: ``ViewWikiTests``, ``ViewPageTests``,
+``AddPageTests``, and ``EditPageTests``. These test the ``view_wiki``,
+``view_page``, ``add_page``, and ``edit_page`` views respectively.
+
+
+Functional tests
+================
+
+We test the whole application, covering security aspects that are not
+tested in the unit tests, like logging in, logging out, checking that
+the ``viewer`` user cannot add or edit pages, but the ``editor`` user
+can, and so on.
+
+Viewing the results of all our edits to ``tests.py``
+====================================================
+
+Once we're done with the ``tests.py`` module, it will look a lot like the
+below:
+
+.. literalinclude:: src/tests/tutorial/tests.py
+ :linenos:
+ :language: python
+
+Running the Tests
+=================
+
+We can run these tests by using ``setup.py test`` in the same way we did in
+:ref:`running_tests`. Assuming our shell's current working directory is the
+"tutorial" distribution directory:
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/python setup.py test -q
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
+
+The expected result looks something like:
+
+.. code-block:: text
+
+ .........
+ ----------------------------------------------------------------------
+ Ran 9 tests in 0.203s
+
+ OK
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 64cab30db..19d438fad 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -76,7 +76,14 @@ For any :app:`Pyramid` application to perform authorization, we need to add a
We'll change our ``__init__.py`` file to enable an
``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable
-declarative security checking.
+declarative security checking. We need to import the new policies:
+
+.. literalinclude:: src/authorization/tutorial/__init__.py
+ :lines: 2-3,8
+ :linenos:
+ :language: python
+
+Then, we'll add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 15-21
@@ -97,25 +104,32 @@ We'll also change ``__init__.py``, adding a call to
:term:`view callable`. This is also known as a :term:`forbidden view`:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 24-26
+ :lines: 24-26,41-43
:linenos:
:language: python
A forbidden view configures our newly created login view to show up when
:app:`Pyramid` detects that a view invocation can not be authorized.
-We'll also add ``view_permission`` arguments with the value ``edit`` to the
-``edit_page`` and ``add_page`` routes. This indicates that the view
-callables which these routes reference cannot be invoked without the
+A ``logout`` :term:`view callable` will allow users to log out later:
+
+.. literalinclude:: src/authorization/tutorial/__init__.py
+ :lines: 27-28
+ :linenos:
+ :language: python
+
+We'll also add ``permission`` arguments with the value ``edit`` to the
+``edit_page`` and ``add_page`` views. This indicates that the view
+callables which these views reference cannot be invoked without the
authenticated user possessing the ``edit`` permission with respect to the
current context.
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 32-39
+ :lines: 37-40
:linenos:
:language: python
-Adding these ``view_permission`` arguments causes Pyramid to make the
+Adding these ``permission`` arguments causes Pyramid to make the
assertion that only users who possess the effective ``edit`` permission at
the time of the request may invoke those two views. We've granted the
``group:editors`` principal the ``edit`` permission at the root model via its
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 0dbcf6684..82e112c64 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -81,28 +81,34 @@ via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 13-14
+ :lines: 13
:language: py
Since this route has a ``pattern`` equalling ``/`` it is the route that will
-be called when the URL ``/`` is visted, e.g. ``http://localhost:6543/``. The
-argument named ``view`` with the value ``tutorial.views.my_view`` is the
+be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``.
+
+Mapping the ``home`` route to code is done by registering a view. You will
+use :meth:`pyramid.config.Configurator.add_view` in :term:`URL dispatch` to
+register views for the routes, mapping your patterns to code:
+
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :lines: 14
+ :language: py
+
+The first positional ``add_view`` argument ``tutorial.views.my_view`` is the
dotted name to a *function* we write (generated by the
``pyramid_routesalchemy`` scaffold) that is given a ``request`` object and
-which returns a response or a dictionary.
-
-You will use :meth:`pyramid.config.Configurator.add_route` statements in a
-:term:`URL dispatch` based application to map URLs to code. This route also
-names a ``view_renderer``, which is a template which lives in the
-``templates`` subdirectory of the package. When the
-``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` will
-use this template to create a response.
+which returns a response or a dictionary. This view also names a
+``renderer``, which is a template which lives in the ``templates``
+subdirectory of the package. When the ``tutorial.views.my_view`` view
+returns a dictionary, a :term:`renderer` will use this template to create a
+response. This
-Fimnally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app`
+Finally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app`
method to return a :term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 15
+ :lines: 16
:language: py
Our final ``__init__.py`` file will look like this:
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst
index 1661753c1..e5d283125 100644
--- a/docs/tutorials/wiki2/definingmodels.rst
+++ b/docs/tutorials/wiki2/definingmodels.rst
@@ -26,6 +26,14 @@ The first thing we want to do is remove the stock ``MyModel`` class from the
generated ``models.py`` file. The ``MyModel`` class is only a sample and
we're not going to use it.
+Next, we'll remove the :class:`sqlalchemy.Unicode` import and replace it
+with :class:`sqlalchemy.Text`.
+
+.. literalinclude:: src/models/tutorial/models.py
+ :lines: 5
+ :linenos:
+ :language: py
+
Then, we'll add a ``Page`` class. Because this is a SQLAlchemy
application, this class should inherit from an instance of
:class:`sqlalchemy.ext.declarative.declarative_base`. Declarative
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index c5a452d11..832f90b92 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -272,8 +272,8 @@ Mapping Views to URLs in ``__init__.py``
========================================
The ``__init__.py`` file contains
-:meth:`pyramid.config.Configurator.add_route` calls which serve to map
-URLs via :term:`url dispatch` to view functions. First, we’ll get rid of the
+:meth:`pyramid.config.Configurator.add_view` calls which serve to map
+routes via :term:`url dispatch` to views. First, we’ll get rid of the
existing route created by the template using the name ``home``. It’s only an
example and isn’t relevant to our application.
@@ -282,21 +282,33 @@ these declarations is very important. ``route`` declarations are matched in
the order they're found in the ``__init__.py`` file.
#. Add a declaration which maps the pattern ``/`` (signifying the root URL)
- to the view named ``view_wiki`` in our ``views.py`` file with the name
- ``view_wiki``. This is the :term:`default view` for the wiki.
+ to the route named ``view_wiki``.
-#. Add a declaration which maps the pattern ``/{pagename}`` to the view named
- ``view_page`` in our ``views.py`` file with the view name ``view_page``.
- This is the regular view for a page.
+#. Add a declaration which maps the pattern ``/{pagename}`` to the route named
+ ``view_page``. This is the regular view for a page.
-#. Add a declaration which maps the pattern
- ``/add_page/{pagename}`` to the view named ``add_page`` in our
- ``views.py`` file with the name ``add_page``. This is the add view
- for a new page.
+#. Add a declaration which maps the pattern ``/add_page/{pagename}`` to the
+ route named ``add_page``. This is the add view for a new page.
#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the
- view named ``edit_page`` in our ``views.py`` file with the name
- ``edit_page``. This is the edit view for a page.
+ route named ``edit_page``. This is the edit view for a page.
+
+After we've defined the routes for our application, we can register views
+to handle the processing and rendering that needs to happen when each route is
+requested.
+
+#. Add a declaration which maps the ``view_wiki`` route to the view named
+ ``view_wiki`` in our ``views.py`` file. This is the :term:`default view`
+ for the wiki.
+
+#. Add a declaration which maps the ``view_page`` route to the view named
+ ``view_page`` in our ``views.py`` file.
+
+#. Add a declaration which maps the ``add_page`` route to the view named
+ ``add_page`` in our ``views.py`` file.
+
+#. Add a declaration which maps the ``edit_page`` route to the view named
+ ``edit_page`` in our ``views.py`` file.
As a result of our edits, the ``__init__.py`` file should look
something like so:
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 025b94927..e8baa568c 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -20,25 +20,26 @@ def main(global_config, **settings):
authentication_policy=authn_policy,
authorization_policy=authz_policy)
config.add_static_view('static', 'tutorial:static')
- config.add_route('view_wiki', '/', view='tutorial.views.view_wiki')
- config.add_route('login', '/login',
- view='tutorial.login.login',
- view_renderer='tutorial:templates/login.pt')
- config.add_route('logout', '/logout',
- view='tutorial.login.logout')
- config.add_route('view_page', '/{pagename}',
- view='tutorial.views.view_page',
- view_renderer='tutorial:templates/view.pt')
- config.add_route('add_page', '/add_page/{pagename}',
- view='tutorial.views.add_page',
- view_renderer='tutorial:templates/edit.pt',
- view_permission='edit')
- config.add_route('edit_page', '/{pagename}/edit_page',
- view='tutorial.views.edit_page',
- view_renderer='tutorial:templates/edit.pt',
- view_permission='edit')
+
+ config.add_route('view_wiki', '/')
+ config.add_route('login', '/login')
+ config.add_route('logout', '/logout')
+ config.add_route('view_page', '/{pagename}')
+ config.add_route('add_page', '/add_page/{pagename}')
+ config.add_route('edit_page', '/{pagename}/edit_page')
+ config.add_route('view_wiki', '/')
+
+ config.add_view('tutorial.login.login', route_name='login',
+ renderer='tutorial:templates/login.pt')
+ config.add_view('tutorial.login.logout', route_name='logout')
+ config.add_view('tutorial.views.view_page', route_name='view_page',
+ renderer='tutorial:templates/view.pt')
+ config.add_view('tutorial.views.add_page', route_name='add_page',
+ renderer='tutorial:templates/edit.pt', permission='edit')
+ config.add_view('tutorial.views.edit_page', route_name='edit_page',
+ renderer='tutorial:templates/edit.pt', permission='edit')
config.add_view('tutorial.login.login',
- renderer='tutorial:templates/login.pt',
- context='pyramid.exceptions.Forbidden')
+ context='pyramid.exceptions.Forbidden',
+ renderer='tutorial:templates/login.pt')
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
index d27b891c0..c74f07652 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -10,8 +10,9 @@ def main(global_config, **settings):
initialize_sql(engine)
config = Configurator(settings=settings)
config.add_static_view('static', 'tutorial:static')
- config.add_route('home', '/', view='tutorial.views.my_view',
- view_renderer='templates/mytemplate.pt')
+ config.add_route('home', '/')
+ config.add_view('tutorial.views.my_view', route_name='home',
+ renderer='templates/mytemplate.pt')
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
index c912a015b..ecc41ca9f 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -10,6 +10,7 @@ def main(global_config, **settings):
initialize_sql(engine)
config = Configurator(settings=settings)
config.add_static_view('static', 'tutorial:static')
- config.add_route('home', '/', view='tutorial.views.my_view',
- view_renderer='templates/mytemplate.pt')
+ config.add_route('home', '/')
+ config.add_view('tutorial.views.my_view', route_name='home',
+ renderer='templates/mytemplate.pt')
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index 1a8d24499..ad89c124e 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -10,15 +10,16 @@ def main(global_config, **settings):
initialize_sql(engine)
config = Configurator(settings=settings)
config.add_static_view('static', 'tutorial:static')
- config.add_route('view_wiki', '/', view='tutorial.views.view_wiki')
- config.add_route('view_page', '/{pagename}',
- view='tutorial.views.view_page',
- view_renderer='tutorial:templates/view.pt')
- config.add_route('add_page', '/add_page/{pagename}',
- view='tutorial.views.add_page',
- view_renderer='tutorial:templates/edit.pt')
- config.add_route('edit_page', '/{pagename}/edit_page',
- view='tutorial.views.edit_page',
- view_renderer='tutorial:templates/edit.pt')
+ config.add_route('view_wiki', '/')
+ config.add_route('view_page', '/{pagename}')
+ config.add_route('add_page', '/add_page/{pagename}')
+ config.add_route('edit_page', '/{pagename}/edit_page')
+ config.add_view('tutorial.views.view_wiki', route_name='view_wiki')
+ config.add_view('tutorial.views.view_page', route_name='view_page',
+ renderer='tutorial:templates/view.pt')
+ config.add_view('tutorial.views.add_page', route_name='add_page',
+ renderer='tutorial:templates/edit.pt')
+ config.add_view('tutorial.views.edit_page', route_name='edit_page',
+ renderer='tutorial:templates/edit.pt')
return config.make_wsgi_app()