summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki2/definingviews.rst
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2016-02-29 13:02:13 -0600
committerMichael Merickel <michael@merickel.org>2016-02-29 13:02:13 -0600
commit565fa59e7eab1e71ec4c4c1ee5773f2ecbf7a07f (patch)
tree9f7098358c01622fcae8fbe8a318f6484e5d3c8b /docs/tutorials/wiki2/definingviews.rst
parentcb98a90c3dcc40dc42813143a601ef631249f5f4 (diff)
parenta79bac6697fa312cd1ba0ac4e784bbc820c5e603 (diff)
downloadpyramid-565fa59e7eab1e71ec4c4c1ee5773f2ecbf7a07f.tar.gz
pyramid-565fa59e7eab1e71ec4c4c1ee5773f2ecbf7a07f.tar.bz2
pyramid-565fa59e7eab1e71ec4c4c1ee5773f2ecbf7a07f.zip
Merge pull request #2024 from Pylons/feature/alchemy-scaffold-update
alchemy scaffold updates
Diffstat (limited to 'docs/tutorials/wiki2/definingviews.rst')
-rw-r--r--docs/tutorials/wiki2/definingviews.rst492
1 files changed, 300 insertions, 192 deletions
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index 0b495445a..b0cbe7dc4 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -3,10 +3,10 @@ Defining Views
==============
A :term:`view callable` in a :app:`Pyramid` application is typically a simple
-Python function that accepts a single parameter named :term:`request`. A
-view callable is assumed to return a :term:`response` object.
+Python function that accepts a single parameter named :term:`request`. A view
+callable is assumed to return a :term:`response` object.
-The request object has a dictionary as an attribute named ``matchdict``. A
+The request object has a dictionary as an attribute named ``matchdict``. A
``matchdict`` maps the placeholders in the matching URL ``pattern`` to the
substrings of the path in the :term:`request` URL. For instance, if a call to
:meth:`pyramid.config.Configurator.add_route` has the pattern ``/{one}/{two}``,
@@ -14,13 +14,13 @@ and a user visits ``http://example.com/foo/bar``, our pattern would be matched
against ``/foo/bar`` and the ``matchdict`` would look like ``{'one':'foo',
'two':'bar'}``.
-Declaring Dependencies in Our ``setup.py`` File
-===============================================
-The view code in our application will depend on a package which is not a
-dependency of the original "tutorial" application. The original "tutorial"
-application was generated by the ``pcreate`` command; it doesn't know
-about our custom application requirements.
+Adding the ``docutils`` dependency
+==================================
+
+Remember in the previous chapter we added a new dependency of the ``bcrypt``
+package. Again, the view code in our application will depend on a package which
+is not a dependency of the original "tutorial" application.
We need to add a dependency on the ``docutils`` package to our ``tutorial``
package's ``setup.py`` file by assigning this dependency to the ``requires``
@@ -30,109 +30,164 @@ Open ``tutorial/setup.py`` and edit it to look like the following:
.. literalinclude:: src/views/setup.py
:linenos:
- :emphasize-lines: 20
+ :emphasize-lines: 13
:language: python
Only the highlighted line needs to be added.
-Running ``setup.py develop``
-============================
+Again, as we did in the previous chapter, the dependency now needs to be
+installed, so re-run the ``python setup.py develop`` command.
+
+
+Static assets
+-------------
+
+Our templates name static assets, including CSS and images. We don't need
+to create these files within our package's ``static`` directory because they
+were provided at the time we created the project.
+
+As an example, the CSS file will be accessed via
+``http://localhost:6543/static/theme.css`` by virtue of the call to the
+``add_static_view`` directive we've made in the ``routes.py`` file. Any number
+and type of static assets can be placed in this directory (or subdirectories)
+and are just referred to by URL or by using the convenience method
+``static_url``, e.g., ``request.static_url('<package>:static/foo.css')`` within
+templates.
+
+
+Adding routes to ``routes.py``
+==============================
-Since a new software dependency was added, you will need to run ``python
-setup.py develop`` again inside the root of the ``tutorial`` package to obtain
-and register the newly added dependency distribution.
+This is the `URL Dispatch` tutorial, so let's start by adding some URL patterns
+to our app. Later we'll attach views to handle the URLs.
-Make sure your current working directory is the root of the project (the
-directory in which ``setup.py`` lives) and execute the following command.
+The ``routes.py`` file contains :meth:`pyramid.config.Configurator.add_route`
+calls which serve to add routes to our application. 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.
-On UNIX:
+We then need to add four calls to ``add_route``. Note that the *ordering* of
+these declarations is very important. Route declarations are matched in the
+order they're registered.
-.. code-block:: text
+#. Add a declaration which maps the pattern ``/`` (signifying the root URL) to
+ the route named ``view_wiki``. In the next step, we will map it to our
+ ``view_wiki`` view callable by virtue of the ``@view_config`` decorator
+ attached to the ``view_wiki`` view function, which in turn will be indicated
+ by ``route_name='view_wiki'``.
- $ cd tutorial
- $ $VENV/bin/python setup.py develop
+#. Add a declaration which maps the pattern ``/{pagename}`` to the route named
+ ``view_page``. This is the regular view for a page. Again, in the next step,
+ we will map it to our ``view_page`` view callable by virtue of the
+ ``@view_config`` decorator attached to the ``view_page`` view function,
+ whin in turn will be indicated by ``route_name='view_page'``.
-On Windows:
+#. 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. We will map
+ it to our ``add_page`` view callable by virtue of the ``@view_config``
+ decorator attached to the ``add_page`` view function, which in turn will be
+ indicated by ``route_name='add_page'``.
-.. code-block:: text
+#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the
+ route named ``edit_page``. This is the edit view for a page. We will map it
+ to our ``edit_page`` view callable by virtue of the ``@view_config``
+ decorator attached to the ``edit_page`` view function, which in turn will be
+ indicated by ``route_name='edit_page'``.
- c:\pyramidtut> cd tutorial
- c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py develop
+As a result of our edits, the ``routes.py`` file should look like the
+following:
-Success executing this command will end with a line to the console something
-like::
+.. literalinclude:: src/views/tutorial/routes.py
+ :linenos:
+ :emphasize-lines: 3-6
+ :language: python
- Finished processing dependencies for tutorial==0.0
+The highlighted lines are the ones that need to be added or edited.
-Adding view functions in ``views.py``
-=====================================
+.. warning::
-It's time for a major change. Open ``tutorial/tutorial/views.py`` and edit it
-to look like the following:
+ The order of the routes is important! If you placed
+ ``/{pagename}/edit_page`` *before* ``/add_page/{pagename}``, then we would
+ never be able to add pages. This is because the first route would always
+ match a request to ``/add_page/edit_page`` whereas we want ``/add_page/..``
+ to have priority. This isn't a huge problem in this particular app because
+ wiki pages are always camel case, but it's important to be aware of this
+ behavior in your own apps.
-.. literalinclude:: src/views/tutorial/views.py
+
+Adding view functions in ``views/default.py``
+=============================================
+
+It's time for a major change. Open ``tutorial/views/default.py`` and
+edit it to look like the following:
+
+.. literalinclude:: src/views/tutorial/views/default.py
:linenos:
:language: python
- :emphasize-lines: 1-7,14,16-72
+ :emphasize-lines: 1-9,12-
The highlighted lines need to be added or edited.
-We added some imports and created a regular expression to find "WikiWords".
+We added some imports, and created a regular expression to find "WikiWords".
We got rid of the ``my_view`` view function and its decorator that was added
when we originally rendered the ``alchemy`` scaffold. It was only an example
-and isn't relevant to our application.
+and isn't relevant to our application. We also deleted the ``db_err_msg``
+string.
-Then we added four :term:`view callable` functions to our ``views.py``
-module:
+Then we added four :term:`view callable` functions to our ``views/default.py``
+module, as mentioned in the previous step:
* ``view_wiki()`` - Displays the wiki itself. It will answer on the root URL.
* ``view_page()`` - Displays an individual page.
-* ``add_page()`` - Allows the user to add a page.
* ``edit_page()`` - Allows the user to edit a page.
+* ``add_page()`` - Allows the user to add a page.
We'll describe each one briefly in the following sections.
.. note::
- There is nothing special about the filename ``views.py``. A project may
- have many view callables throughout its codebase in arbitrarily named
- files. Files implementing view callables often have ``view`` in their
- filenames (or may live in a Python subpackage of your application package
- named ``views``), but this is only by convention.
+ There is nothing special about the filename ``default.py`` exept that it is a
+ Python module. A project may have many view callables throughout its codebase
+ in arbitrarily named modules. Modules implementing view callables often have
+ ``view`` in their name (or may live in a Python subpackage of your
+ application package named ``views``, as in our case), but this is only by
+ convention, not a requirement.
+
The ``view_wiki`` view function
-------------------------------
Following is the code for the ``view_wiki`` view function and its decorator:
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 20-24
- :lineno-start: 20
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 17-20
+ :lineno-match:
:linenos:
:language: python
``view_wiki()`` is the :term:`default view` that gets called when a request is
-made to the root URL of our wiki. It always redirects to an URL which
+made to the root URL of our wiki. It always redirects to a URL which
represents the path to our "FrontPage".
The ``view_wiki`` view callable always redirects to the URL of a Page resource
named "FrontPage". To do so, it returns an instance of the
:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
the :class:`pyramid.interfaces.IResponse` interface, like
-:class:`pyramid.response.Response` does). It uses the
-:meth:`pyramid.request.Request.route_url` API to construct an URL to the
+:class:`pyramid.response.Response`). It uses the
+:meth:`pyramid.request.Request.route_url` API to construct a URL to the
``FrontPage`` page (i.e., ``http://localhost:6543/FrontPage``), and uses it as
the "location" of the ``HTTPFound`` response, forming an HTTP redirect.
+
The ``view_page`` view function
-------------------------------
Here is the code for the ``view_page`` view function and its decorator:
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 25-45
- :lineno-start: 25
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 22-42
+ :lineno-match:
:linenos:
:language: python
@@ -141,83 +196,59 @@ Here is the code for the ``view_page`` view function and its decorator:
``Page`` model object) as HTML. Then it substitutes an HTML anchor for each
*WikiWord* reference in the rendered HTML using a compiled regular expression.
-The curried function named ``check`` is used as the first argument to
+The curried function named ``add_link`` is used as the first argument to
``wikiwords.sub``, indicating that it should be called to provide a value for
each WikiWord match found in the content. If the wiki already contains a
-page with the matched WikiWord name, ``check()`` generates a view
+page with the matched WikiWord name, ``add_link()`` generates a view
link to be used as the substitution value and returns it. If the wiki does
-not already contain a page with the matched WikiWord name, ``check()``
+not already contain a page with the matched WikiWord name, ``add_link()``
generates an "add" link as the substitution value and returns it.
As a result, the ``content`` variable is now a fully formed bit of HTML
containing various view and add links for WikiWords based on the content of
our current page object.
-We then generate an edit URL because it's easier to do here than in the
+We then generate an edit URL, because it's easier to do here than in the
template, and we return a dictionary with a number of arguments. The fact that
``view_page()`` returns a dictionary (as opposed to a :term:`response` object)
is a cue to :app:`Pyramid` that it should try to use a :term:`renderer`
associated with the view configuration to render a response. In our case, the
-renderer used will be the ``templates/view.pt`` template, as indicated in the
-``@view_config`` decorator that is applied to ``view_page()``.
+renderer used will be the ``view.jinja2`` template, as indicated in
+the ``@view_config`` decorator that is applied to ``view_page()``.
-The ``add_page`` view function
-------------------------------
+If the page does not exist, then we need to handle that by raising a
+:class:`pyramid.httpexceptions.HTTPNotFound` to trigger our 404 handling,
+defined in ``tutorial/views/notfound.py``.
-Here is the code for the ``add_page`` view function and its decorator:
-
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 47-58
- :lineno-start: 47
- :linenos:
- :language: python
-
-``add_page()`` is invoked when a user clicks on a *WikiWord* which
-isn't yet represented as a page in the system. The ``check`` function
-within the ``view_page`` view generates URLs to this view.
-``add_page()`` also acts as a handler for the form that is generated
-when we want to add a page object. The ``matchdict`` attribute of the
-request passed to the ``add_page()`` view will have the values we need
-to construct URLs and find model objects.
-
-The ``matchdict`` will have a ``'pagename'`` key that matches the name of
-the page we'd like to add. If our add view is invoked via,
-e.g., ``http://localhost:6543/add_page/SomeName``, the value for
-``'pagename'`` in the ``matchdict`` will be ``'SomeName'``.
+.. note::
-If the view execution *is* a result of a form submission (i.e., the expression
-``'form.submitted' in request.params`` is ``True``), we grab the page body
-from the form data, create a Page object with this page body and the name
-taken from ``matchdict['pagename']``, and save it into the database using
-``DBSession.add``. We then redirect back to the ``view_page`` view for the
-newly created page.
+ Using ``raise`` versus ``return`` with the HTTP exceptions is an important
+ distinction that can commonly mess people up. In
+ ``tutorial/views/notfound.py`` there is an :term:`exception view`
+ registered for handling the ``HTTPNotFound`` exception. Exception views are
+ only triggered for raised exceptions. If the ``HTTPNotFound`` is returned,
+ then it has an internal "stock" template that it will use to render itself
+ as a response. If you aren't seeing your exception view being executed, this
+ is most likely the problem! See :ref:`special_exceptions_in_callables` for
+ more information about exception views.
-If the view execution is *not* a result of a form submission (i.e., the
-expression ``'form.submitted' in request.params`` is ``False``), the view
-callable renders a template. To do so, it generates a ``save_url`` which the
-template uses as the form post URL during rendering. We're lazy here, so
-we're going to use the same template (``templates/edit.pt``) for the add
-view as well as the page edit view. To do so we create a dummy Page object
-in order to satisfy the edit form's desire to have *some* page object
-exposed as ``page``. :app:`Pyramid` will render the template associated
-with this view to a response.
The ``edit_page`` view function
-------------------------------
Here is the code for the ``edit_page`` view function and its decorator:
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 60-72
- :lineno-start: 60
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 44-56
+ :lineno-match:
:linenos:
:language: python
-``edit_page()`` is invoked when a user clicks the "Edit this
-Page" button on the view form. It renders an edit form but it also acts as
-the handler for the form it renders. The ``matchdict`` attribute of the
-request passed to the ``edit_page`` view will have a ``'pagename'`` key
-matching the name of the page the user wants to edit.
+``edit_page()`` is invoked when a user clicks the "Edit this Page" button on
+the view form. It renders an edit form, but it also acts as the handler for the
+form which it renders. The ``matchdict`` attribute of the request passed to the
+``edit_page`` view will have a ``'pagename'`` key matching the name of the page
+that the user wants to edit.
If the view execution *is* a result of a form submission (i.e., the expression
``'form.submitted' in request.params`` is ``True``), the view grabs the
@@ -230,120 +261,190 @@ expression ``'form.submitted' in request.params`` is ``False``), the view
simply renders the edit form, passing the page object and a ``save_url``
which will be used as the action of the generated form.
+.. note::
+
+ Since our ``request.dbsession`` defined in the previous chapter is
+ registered with the ``pyramid_tm`` transaction manager, any changes we make
+ to objects managed by the that session will be committed automatically. In
+ the event that there was an error (even later, in our template code), the
+ changes would be aborted. This means the view itself does not need to
+ concern itself with commit/rollback logic.
+
+
+The ``add_page`` view function
+------------------------------
+
+Here is the code for the ``add_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 58-
+ :lineno-match:
+ :linenos:
+ :language: python
+
+``add_page()`` is invoked when a user clicks on a *WikiWord* which isn't yet
+represented as a page in the system. The ``add_link`` function within the
+``view_page`` view generates URLs to this view. ``add_page()`` also acts as a
+handler for the form that is generated when we want to add a page object. The
+``matchdict`` attribute of the request passed to the ``add_page()`` view will
+have the values we need to construct URLs and find model objects.
+
+The ``matchdict`` will have a ``'pagename'`` key that matches the name of the
+page we'd like to add. If our add view is invoked via, for example,
+``http://localhost:6543/add_page/SomeName``, the value for ``'pagename'`` in
+the ``matchdict`` will be ``'SomeName'``.
+
+Next a check is performed to determine whether the ``Page`` already exists in
+the database. If it already exists, then the client is redirected to the
+``edit_page`` view, else we continue to the next check.
+
+If the view execution *is* a result of a form submission (i.e., the expression
+``'form.submitted' in request.params`` is ``True``), we grab the page body from
+the form data, create a Page object with this page body and the name taken from
+``matchdict['pagename']``, and save it into the database using
+``request.dbession.add``. Since we have not yet covered authentication, we
+don't have a logged-in user to add as the page's ``creator``. Until we get to
+that point in the tutorial, we'll just assume that all pages are created by the
+``editor`` user. Thus we query for that object, and set it on ``page.creator``.
+Finally, we redirect the client back to the ``view_page`` view for the newly
+created page.
+
+If the view execution is *not* a result of a form submission (i.e., the
+expression ``'form.submitted' in request.params`` is ``False``), the view
+callable renders a template. To do so, it generates a ``save_url`` which the
+template uses as the form post URL during rendering. We're lazy here, so
+we're going to use the same template (``templates/edit.jinja2``) for the add
+view as well as the page edit view. To do so we create a dummy ``Page`` object
+in order to satisfy the edit form's desire to have *some* page object
+exposed as ``page``. :app:`Pyramid` will render the template associated
+with this view to a response.
+
+
Adding templates
================
The ``view_page``, ``add_page`` and ``edit_page`` views that we've added
-reference a :term:`template`. Each template is a :term:`Chameleon`
-:term:`ZPT` template. These templates will live in the ``templates``
-directory of our tutorial package. Chameleon templates must have a ``.pt``
-extension to be recognized as such.
+reference a :term:`template`. Each template is a :term:`Jinja2` template.
+These templates will live in the ``templates`` directory of our tutorial
+package. Jinja2 templates must have a ``.jinja2`` extension to be recognized
+as such.
+
-The ``view.pt`` template
-------------------------
+The ``layout.jinja2`` template
+------------------------------
-Create ``tutorial/tutorial/templates/view.pt`` and add the following
-content:
+Update ``tutorial/templates/layout.jinja2`` with the following content, as
+indicated by the emphasized lines:
-.. literalinclude:: src/views/tutorial/templates/view.pt
+.. literalinclude:: src/views/tutorial/templates/layout.jinja2
:linenos:
+ :emphasize-lines: 11,36
:language: html
-This template is used by ``view_page()`` for displaying a single
-wiki page. It includes:
+Since we're using a templating engine, we can factor common boilerplate out of
+our page templates into reusable components. One method for doing this is
+template inheritance via blocks.
-- A ``div`` element that is replaced with the ``content`` value provided by
- the view (lines 36-38). ``content`` contains HTML, so the ``structure``
- keyword is used to prevent escaping it (i.e., changing ">" to "&gt;", etc.)
-- A link that points at the "edit" URL which invokes the ``edit_page`` view
- for the page being viewed (lines 40-42).
+- We have defined two placeholders in the layout template where a child
+ template can override the content. These blocks are named ``subtitle`` (line
+ 11) and ``content`` (line 36).
+- Please refer to the Jinja2_ documentation for more information about template
+ inheritance.
-The ``edit.pt`` template
-------------------------
-Create ``tutorial/tutorial/templates/edit.pt`` and add the following
-content:
+The ``view.jinja2`` template
+----------------------------
-.. literalinclude:: src/views/tutorial/templates/edit.pt
+Create ``tutorial/templates/view.jinja2`` and add the following content:
+
+.. literalinclude:: src/views/tutorial/templates/view.jinja2
:linenos:
:language: html
-This template is used by ``add_page()`` and ``edit_page()`` for adding and
-editing a wiki page. It displays a page containing a form that includes:
+This template is used by ``view_page()`` for displaying a single wiki page.
-- A 10 row by 60 column ``textarea`` field named ``body`` that is filled
- with any existing page data when it is rendered (line 45).
-- A submit button that has the name ``form.submitted`` (line 48).
+- We begin by extending the ``layout.jinja2`` template defined above, which
+ provides the skeleton of the page (line 1).
+- We override the ``subtitle`` block from the base layout, inserting the page
+ name into the page's title (line 3).
+- We override the ``content`` block from the base layout to insert our markup
+ into the body (lines 5-18).
+- We use a variable that is replaced with the ``content`` value provided by the
+ view (line 6). ``content`` contains HTML, so the ``|safe`` filter is used to
+ prevent escaping it (e.g., changing ">" to "&gt;").
+- We create a link that points at the "edit" URL, which when clicked invokes
+ the ``edit_page`` view for the requested page (line 9).
-The form POSTs back to the ``save_url`` argument supplied by the view (line
-43). The view will use the ``body`` and ``form.submitted`` values.
-.. note:: Our templates use a ``request`` object that none of our tutorial
- views return in their dictionary. ``request`` is one of several names that
- are available "by default" in a template when a template renderer is used.
- See :ref:`renderer_system_values` for information about other names that
- are available by default when a template is used as a renderer.
+The ``edit.jinja2`` template
+----------------------------
-Static Assets
--------------
+Create ``tutorial/templates/edit.jinja2`` and add the following content:
-Our templates name static assets, including CSS and images. We don't need
-to create these files within our package's ``static`` directory because they
-were provided at the time we created the project.
+.. literalinclude:: src/views/tutorial/templates/edit.jinja2
+ :linenos:
+ :emphasize-lines: 1,3,12,14,17
+ :language: html
-As an example, the CSS file will be accessed via
-``http://localhost:6543/static/theme.css`` by virtue of the call to the
-``add_static_view`` directive we've made in the ``__init__.py`` file. Any
-number and type of static assets can be placed in this directory (or
-subdirectories) and are just referred to by URL or by using the convenience
-method ``static_url``, e.g.,
-``request.static_url('<package>:static/foo.css')`` within templates.
-
-Adding Routes to ``__init__.py``
-================================
-
-The ``__init__.py`` file contains
-:meth:`pyramid.config.Configurator.add_route` calls which serve to add routes
-to our application. 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.
-
-We then need to add four calls to ``add_route``. Note that the *ordering* of
-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 route named ``view_wiki``. It maps to our ``view_wiki`` view
- callable by virtue of the ``@view_config`` attached to the ``view_wiki``
- view function indicating ``route_name='view_wiki'``.
+This template serves two use cases. It is used by ``add_page()`` and
+``edit_page()`` for adding and editing a wiki page. It displays a page
+containing a form and which provides the following:
-#. Add a declaration which maps the pattern ``/{pagename}`` to the route named
- ``view_page``. This is the regular view for a page. It maps
- to our ``view_page`` view callable by virtue of the ``@view_config``
- attached to the ``view_page`` view function indicating
- ``route_name='view_page'``.
+- Again, we extend the ``layout.jinja2`` template, which provides the skeleton
+ of the page (line 1).
+- Override the ``subtitle`` block to affect the ``<title>`` tag in the
+ ``head`` of the page (line 3).
+- A 10-row by 60-column ``textarea`` field named ``body`` that is filled with
+ any existing page data when it is rendered (line 14).
+- A submit button that has the name ``form.submitted`` (line 17).
+- The form POSTs back to the ``save_url`` argument supplied by the view (line
+ 12). The view will use the ``body`` and ``form.submitted`` values.
-#. 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. It maps
- to our ``add_page`` view callable by virtue of the ``@view_config``
- attached to the ``add_page`` view function indicating
- ``route_name='add_page'``.
-#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the
- route named ``edit_page``. This is the edit view for a page. It maps
- to our ``edit_page`` view callable by virtue of the ``@view_config``
- attached to the ``edit_page`` view function indicating
- ``route_name='edit_page'``.
+The ``404.jinja2`` template
+---------------------------
+
+Replace ``tutorial/templates/404.jinja2`` with the following content:
+
+.. literalinclude:: src/views/tutorial/templates/404.jinja2
+ :linenos:
+ :language: html
-As a result of our edits, the ``__init__.py`` file should look
-something like:
+This template is linked from the ``notfound_view`` defined in
+``tutorial/views/notfound.py`` as shown here:
-.. literalinclude:: src/views/tutorial/__init__.py
+.. literalinclude:: src/views/tutorial/views/notfound.py
:linenos:
- :emphasize-lines: 19-22
+ :emphasize-lines: 6
:language: python
-The highlighted lines are the ones that need to be added or edited.
+There are several important things to note about this configuration:
+
+- The ``notfound_view`` in the above snippet is called an
+ :term:`exception view`. For more information see
+ :ref:`special_exceptions_in_callables`.
+- The ``notfound_view`` sets the response status to 404. It's possible
+ to affect the response object used by the renderer via
+ :ref:`request_response_attr`.
+- The ``notfound_view`` is registered as an exception view and will be invoked
+ **only** if ``pyramid.httpexceptions.HTTPNotFound`` is raised as an
+ exception. This means it will not be invoked for any responses returned
+ from a view normally. For example, on line 27 of
+ ``tutorial/views/default.py`` the exception is raised which will trigger
+ the view.
+
+Finally, we may delete the ``tutorial/templates/mytemplate.jinja2`` template
+that was provided by the ``alchemy`` scaffold, as we have created our own
+templates for the wiki.
+
+.. note::
+
+ Our templates use a ``request`` object that none of our tutorial
+ views return in their dictionary. ``request`` is one of several names that
+ are available "by default" in a template when a template renderer is used.
+ See :ref:`renderer_system_values` for information about other names that
+ are available by default when a template is used as a renderer.
+
Viewing the application in a browser
====================================
@@ -355,15 +456,22 @@ each of the following URLs, checking that the result is as expected:
- http://localhost:6543/ invokes the ``view_wiki`` view. This always
redirects to the ``view_page`` view of the ``FrontPage`` page object.
-- http://localhost:6543/FrontPage invokes the ``view_page`` view of the front
- page object.
+- http://localhost:6543/FrontPage invokes the ``view_page`` view of the
+ ``FrontPage`` page object.
+
+- http://localhost:6543/FrontPage/edit_page invokes the ``edit_page`` view for
+ the ``FrontPage`` page object.
-- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
- front page object.
+- http://localhost:6543/add_page/SomePageName invokes the ``add_page`` view for
+ a page. If the page already exists, then it redirects the user to the
+ ``edit_page`` view for the page object.
-- http://localhost:6543/add_page/SomePageName invokes the add view for a page.
+- http://localhost:6543/SomePageName/edit_page invokes the ``edit_page`` view
+ for an existing page, or generates an error if the page does not exist.
- To generate an error, visit http://localhost:6543/foobars/edit_page which
will generate a ``NoResultFound: No row was found for one()`` error. You'll
see an interactive traceback facility provided by
:term:`pyramid_debugtoolbar`.
+
+.. _jinja2: http://jinja.pocoo.org/