summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki/definingviews.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/tutorials/wiki/definingviews.rst')
-rw-r--r--docs/tutorials/wiki/definingviews.rst477
1 files changed, 232 insertions, 245 deletions
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index d584a1b41..bd8dc6ecf 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -4,44 +4,37 @@
Defining Views
==============
-A :term:`view callable` in a :term:`traversal`-based :app:`Pyramid`
-application is typically a simple Python function that accepts two
-parameters: :term:`context` and :term:`request`. A view callable is
-assumed to return a :term:`response` object.
+A :term:`view callable` in a :term:`traversal`-based :app:`Pyramid` application is typically a simple Python function that accepts two parameters: :term:`context` and :term:`request`.
+A view callable is assumed to return a :term:`response` object.
.. note::
- A :app:`Pyramid` view can also be defined as callable
- which accepts *only* a :term:`request` argument. You'll see
- this one-argument pattern used in other :app:`Pyramid` tutorials
- and applications. Either calling convention will work in any
- :app:`Pyramid` application; the calling conventions can be used
- interchangeably as necessary. In :term:`traversal`-based applications,
- URLs are mapped to a context :term:`resource`, and since our
- :term:`resource tree` also represents our application's
- "domain model", we're often interested in the context because
- it represents the persistent storage of our application. For
- this reason, in this tutorial we define views as callables that
- accept ``context`` in the callable argument list. If you do
- need the ``context`` within a view function that only takes
- the request as a single argument, you can obtain it via
- ``request.context``.
-
-We're going to define several :term:`view callable` functions, then wire them
-into :app:`Pyramid` using some :term:`view configuration`.
+ A :app:`Pyramid` view can also be defined as callable which accepts *only* a :term:`request` argument.
+ You will see this one-argument pattern used in other :app:`Pyramid` tutorials and applications.
+ Either calling convention will work in any :app:`Pyramid` application.
+ The calling conventions can be used interchangeably as necessary.
+
+ In :term:`traversal`-based applications, URLs are mapped to a context :term:`resource`.
+ Since our :term:`resource tree` also represents our application's "domain model", we are often interested in the context because it represents the persistent storage of our application.
+ For this reason, in this tutorial we define views as callables that accept ``context`` in the callable argument list.
+ If you do need the ``context`` within a view function that only takes the request as a single argument, you can obtain it via ``request.context``.
+
+We will define several :term:`view callable` functions, then wire them into :app:`Pyramid` using some :term:`view configuration`.
+
+.. seealso::
+
+ This chapter will introduce more concepts, as did the previous.
+ See also the chapter :ref:`resources_chapter` for a complete description of resources and the chapter :ref:`traversal_chapter` for the technical details of how traversal works in Pyramid.
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 cookiecutter; it doesn't know
-about our custom application requirements.
+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 cookiecutter.
+It does not know about our custom application requirements.
-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``
-parameter in the ``setup()`` function.
+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`` parameter in the ``setup()`` function.
Open ``setup.py`` and edit it to look like the following:
@@ -52,17 +45,15 @@ Open ``setup.py`` and edit it to look like the following:
Only the highlighted line needs to be added.
+
.. _wiki-running-pip-install:
Running ``pip install -e .``
============================
-Since a new software dependency was added, you will need to run ``pip install
--e .`` again inside the root of the ``tutorial`` package to obtain and register
-the newly added dependency distribution.
+Since a new software dependency was added, you need to run ``pip install -e .`` again inside the root of the ``tutorial`` package to obtain and register the newly added dependency distribution.
-Make sure your current working directory is the root of the project (the
-directory in which ``setup.py`` lives) and execute the following command.
+Make sure your current working directory is the root of the project (the directory in which ``setup.py`` lives) and execute the following command.
On Unix:
@@ -78,237 +69,238 @@ On Windows:
cd tutorial
%VENV%\Scripts\pip install -e .
-Success executing this command will end with a line to the console something
-like:
+Success executing this command will end with a line to the console similar to the following:
.. code-block:: text
- Successfully installed docutils-0.13.1 tutorial
+ Successfully installed docutils-0.14 tutorial
-Adding view functions in ``views.py``
-=====================================
+Adding view functions in the ``views`` package
+==============================================
-It's time for a major change. Open ``tutorial/views.py`` and edit it to look
-like the following:
+It is time for a major change.
+Open ``tutorial/views/default.py`` and edit it to look like the following:
-.. literalinclude:: src/views/tutorial/views.py
+.. literalinclude:: src/views/tutorial/views/default.py
:linenos:
:language: python
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 originally rendered after we selected the ``zodb`` backend option in the
-cookiecutter. It was only an example and isn't relevant to our application.
+We got rid of the ``my_view`` view function and its decorator that was added when originally rendered after we selected the ``zodb`` backend option in the cookiecutter.
+It was only an example and is not relevant to our application.
-Then we added four :term:`view callable` functions to our ``views.py``
-module:
+Then we added four :term:`view callable` functions to our ``views.py`` module:
* ``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.
-We'll describe each one briefly in the following sections.
+We will 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 ``views.py``.
+ A project may have many view callables throughout its codebase in arbitrarily named files.
+ Files that implement view callables often have ``view`` in their names (or may live in a Python subpackage of your application package named ``views``), but this is only by convention.
+
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: 12-14
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 13-15
:lineno-match:
:language: python
-.. note:: In our code, we use an *import* that is *relative* to our package
- named ``tutorial``, meaning we can omit the name of the package in the
- ``import`` and ``context`` statements. In our narrative, however, we refer
- to a *class* and thus we use the *absolute* form, meaning that the name of
- the package is included.
-
-``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
-represents the path to our "FrontPage".
-
-We provide it with a ``@view_config`` decorator which names the class
-``tutorial.models.Wiki`` as its context. This means that when a Wiki resource
-is the context and no :term:`view name` exists in the request, then this view
-will be used. The view configuration associated with ``view_wiki`` does not
-use a ``renderer`` because the view callable always returns a :term:`response`
-object rather than a dictionary. No renderer is necessary when a view returns
-a response object.
-
-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
-``FrontPage`` page resource (i.e., ``http://localhost:6543/FrontPage``), and
-uses it as the "location" of the ``HTTPFound`` response, forming an HTTP
-redirect.
+.. note::
+
+ In our code, we use an *import* that is *relative* to our package named ``tutorial``.
+ This means we can omit the name of the package in the ``import`` and ``context`` statements.
+ In our narrative, however, we refer to a *class* and thus we use the *absolute* form.
+ This means that the name of the package is included.
+
+``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 represents the path to our ``FrontPage``.
+
+We provide it with a ``@view_config`` decorator which names the class ``tutorial.models.Wiki`` as its context.
+This means that when a ``Wiki`` resource is the context and no :term:`view name` exists in the request, then this view will be used.
+The view configuration associated with ``view_wiki`` does not use a ``renderer`` because the view callable always returns a :term:`response` object rather than a dictionary.
+No renderer is necessary when a view returns a response object.
+
+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 this class implement the :class:`pyramid.interfaces.IResponse` interface, similar to :class:`pyramid.response.Response`.
+It uses the :meth:`pyramid.request.Request.route_url` API to construct an URL to the ``FrontPage`` page resource (in other words, ``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: 16-33
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 18-35
:lineno-match:
:language: python
-The ``view_page`` function is configured to respond as the default view
-of a Page resource. We provide it with a ``@view_config`` decorator which
-names the class ``tutorial.models.Page`` as its context. This means that
-when a Page resource is the context, and no :term:`view name` exists in the
-request, this view will be used. We inform :app:`Pyramid` this view will use
-the ``templates/view.pt`` template file as a ``renderer``.
-
-The ``view_page`` function generates the :term:`reStructuredText` body of a
-page (stored as the ``data`` attribute of the context passed to the view; the
-context will be a ``Page`` resource) 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
-``wikiwords.sub``, indicating that it should be called to provide a value for
-each WikiWord match found in the content. If the wiki (our page's
-``__parent__``) already contains a page with the matched WikiWord name, the
-``check`` function 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, the function 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 resource.
-
-We then generate an edit URL because it's easier to do here than in the
-template, and we wrap up a number of arguments in a dictionary and return
-it.
-
-The arguments we wrap into a dictionary include ``page``, ``content``, and
-``edit_url``. As a result, the *template* associated with this view callable
-(via ``renderer=`` in its configuration) will be able to use these names to
-perform various rendering tasks. The template associated with this view
-callable will be a template which lives in ``templates/view.pt``.
-
-Note the contrast between this view callable and the ``view_wiki`` view
-callable. In the ``view_wiki`` view callable, we unconditionally return a
-:term:`response` object. In the ``view_page`` view callable, we return a
-*dictionary*. It is *always* fine to return a :term:`response` object from a
-:app:`Pyramid` view. Returning a dictionary is allowed only when there is a
-:term:`renderer` associated with the view callable in the view configuration.
+The ``view_page`` function is configured to respond as the default view of a ``Page`` resource.
+We provide it with a ``@view_config`` decorator which names the class ``tutorial.models.Page`` as its context.
+This means that when a ``Page`` resource is the context, and no :term:`view name` exists in the request, this view will be used.
+We inform :app:`Pyramid` this view will use the ``templates/view.pt`` template file as a ``renderer``.
+
+The ``view_page`` function generates the :term:`reStructuredText` body of a page as HTML.
+The body is stored as the ``data`` attribute of the context passed to the view.
+The context will be a ``Page`` resource.
+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 ``wikiwords.sub``, indicating that it should be called to provide a value for each ``WikiWord`` match found in the content.
+If the wiki (our page's ``__parent__``) already contains a page with the matched ``WikiWord`` name, the ``check`` function 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, the function generates an "add" link as the substitution value and returns it.
+
+As a result, the ``page_text`` variable is now a fully formed bit of HTML containing various view and add links for ``WikiWord``\s based on the content of our current page resource.
+
+We then generate an edit URL because it is easier to do here than in the template.
+Finally we wrap up a number of arguments in a dictionary and return it.
+
+The arguments we wrap into a dictionary include ``page``, ``page_text``, and ``edit_url``.
+As a result, the *template* associated with this view callable (via ``renderer=`` in its configuration) will be able to use these names to perform various rendering tasks.
+The template associated with this view callable will be a template which lives in ``templates/view.pt``.
+
+Note the contrast between this view callable and the ``view_wiki`` view callable.
+In the ``view_wiki`` view callable, we unconditionally return a :term:`response` object.
+In the ``view_page`` view callable, we return a *dictionary*. It is *always* fine to return a :term:`response` object from a :app:`Pyramid` view.
+Returning a dictionary is allowed only when there is a :term:`renderer` associated with the view callable in the view configuration.
+
The ``add_page`` view function
------------------------------
Here is the code for the ``add_page`` view function and its decorator:
-.. literalinclude:: src/views/tutorial/views.py
- :lines: 35-50
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 38-53
:lineno-match:
:language: python
-The ``add_page`` function is configured to respond when the context resource
-is a Wiki and the :term:`view name` is ``add_page``. We provide it with a
-``@view_config`` decorator which names the string ``add_page`` as its
-:term:`view name` (via ``name=``), the class ``tutorial.models.Wiki`` as its
-context, and the renderer named ``templates/edit.pt``. This means that when a
-Wiki resource is the context, and a :term:`view name` named ``add_page``
-exists as the result of traversal, this view will be used. We inform
-:app:`Pyramid` this view will use the ``templates/edit.pt`` template file as a
-``renderer``. We share the same template between add and edit views, thus
-``edit.pt`` instead of ``add.pt``.
-
-The ``add_page`` function will be 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. It also acts as a
-handler for the form that is generated when we want to add a page resource.
-The ``context`` of the ``add_page`` view is always a Wiki resource (*not* a
-Page resource).
-
-The request :term:`subpath` in :app:`Pyramid` is the sequence of names that
-are found *after* the :term:`view name` in the URL segments given in the
-``PATH_INFO`` of the WSGI request as the result of :term:`traversal`. If our
-add view is invoked via, e.g., ``http://localhost:6543/add_page/SomeName``,
-the :term:`subpath` will be a tuple: ``('SomeName',)``.
-
-The add view takes the zero\ :sup:`th` element of the subpath (the wiki page name),
-and aliases it to the name attribute in order to know the name of the page
-we're trying to add.
-
-If the view rendering is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is ``False``), the view
-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 trying
-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 resource object in
-order to satisfy the edit form's desire to have *some* page object exposed as
-``page``, and we'll render the template to a response.
-
-If the view rendering *is* a result of a form submission (if the expression
-``'form.submitted' in request.params`` is ``True``), we grab the page body
-from the form data, create a Page object using the name in the subpath and
-the page body, and save it into "our context" (the Wiki) using the
-``__setitem__`` method of the context. We then redirect back to the
-``view_page`` view (the default view for a page) for the newly created page.
+The ``add_page`` function is configured to respond when the context resource is a ``Wiki`` and the :term:`view name` is ``add_page``.
+We provide it with a ``@view_config`` decorator which names the string ``add_page`` as its :term:`view name` (via ``name=``), the class ``tutorial.models.Wiki`` as its context, and the renderer named ``templates/edit.pt``.
+This means that when a ``Wiki`` resource is the context, and a :term:`view name` named ``add_page`` exists as the result of traversal, then this view will be used.
+We inform :app:`Pyramid` this view will use the ``templates/edit.pt`` template file as a ``renderer``.
+We share the same template between add and edit views, thus ``edit.pt`` instead of ``add.pt``.
+
+The ``add_page`` function will be invoked when a user clicks on a ``WikiWord`` that is not yet represented as a page in the system.
+The ``check`` function within the ``view_page`` view generates URLs to this view.
+It also acts as a handler for the form that is generated when we want to add a page resource.
+The ``context`` of the ``add_page`` view is always a ``Wiki`` resource (*not* a ``Page`` resource).
+
+The request :term:`subpath` in :app:`Pyramid` is the sequence of names that are found *after* the :term:`view name` in the URL segments given in the ``PATH_INFO`` of the WSGI request as the result of :term:`traversal`.
+If our add view is invoked via, for example, ``http://localhost:6543/add_page/SomeName``, then the :term:`subpath` will be a tuple ``('SomeName',)``.
+
+The add view takes the zero\ :sup:`th` element of the subpath (the wiki page name), then aliases it to the name attribute to know the name of the page we are trying to add.
+
+If the view rendering is *not* a result of a form submission (if the expression ``'form.submitted' in request.params`` is ``False``), then the view renders a template.
+To do so, it generates a ``save_url`` which the template uses as the form post URL during rendering.
+We are lazy here, so we try to use the same template (``templates/edit.pt``) for both the add and edit views.
+To do so, we create a dummy ``Page`` resource object to satisfy the edit form's desire to have *some* page object exposed as ``page``.
+We then set the ``Page`` object's ``__name__`` and ``__parent__``.
+Then we will render the template to a response.
+
+If the view rendering *is* a result of a form submission (if the expression ``'form.submitted' in request.params`` is ``True``), then do the following:
+
+- Grab the page body from the form data as ``body``.
+- Create a ``Page`` object using the name in the subpath and the page body as ``page``.
+- Set the ``Page`` object's ``__name__`` and ``__parent__``.
+- Save it into "our context" (the ``Wiki``) using the ``__setitem__`` method of the context.
+- We then redirect back to the ``view_page`` view (the default view for a page) for the newly created page.
+
+.. seealso::
+
+ In the :ref:`previous chapter <wiki_defining_the_domain_model>`, we mentioned that all objects in a traversal graph must have a ``__name__`` and a ``__parent__`` attribute.
+ That provides location awareness for resources.
+ See also the section on :ref:`location_aware` in the :ref:`resources_chapter` chapter for a complete discussion.
+
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: 52-60
+.. literalinclude:: src/views/tutorial/views/default.py
+ :lines: 56-64
:lineno-match:
:language: python
-The ``edit_page`` function is configured to respond when the context is
-a Page resource and the :term:`view name` is ``edit_page``. We provide it
-with a ``@view_config`` decorator which names the string ``edit_page`` as its
-:term:`view name` (via ``name=``), the class ``tutorial.models.Page`` as its
-context, and the renderer named ``templates/edit.pt``. This means that when
-a Page resource is the context, and a :term:`view name` exists as the result
-of traversal named ``edit_page``, this view will be used. We inform
-:app:`Pyramid` this view will use the ``templates/edit.pt`` template file as
-a ``renderer``.
-
-The ``edit_page`` function will be 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 form post view callable for the form it renders. The ``context`` of the
-``edit_page`` view will *always* be a Page resource (never a Wiki resource).
-
-If the view execution is *not* a result of a form submission (if the
-expression ``'form.submitted' in request.params`` is ``False``), the view
-simply renders the edit form, passing the page resource, and a ``save_url``
-which will be used as the action of the generated form.
-
-If the view execution *is* a result of a form submission (if the expression
-``'form.submitted' in request.params`` is ``True``), the view grabs the
-``body`` element of the request parameter and sets it as the ``data``
-attribute of the page context. It then redirects to the default view of the
-context (the page), which will always be the ``view_page`` view.
+The ``edit_page`` function is configured to respond when the context is a ``Page`` resource and the :term:`view name` is ``edit_page``.
+We provide it with a ``@view_config`` decorator which names the string ``edit_page`` as its :term:`view name` (via ``name=``), the class ``tutorial.models.Page`` as its context, and the renderer named ``templates/edit.pt``.
+This means that when a ``Page`` resource is the context, and a :term:`view name` exists as the result of traversal named ``edit_page``, this view will be used.
+We inform :app:`Pyramid` this view will use the ``templates/edit.pt`` template file as a ``renderer``.
+
+The ``edit_page`` function will be invoked when a user clicks the "Edit this Page" button on the view form.
+It renders an edit form.
+It also acts as the form post view callable for the form it renders.
+The ``context`` of the ``edit_page`` view will *always* be a ``Page`` resource (never a ``Wiki`` resource).
+
+If the view execution is *not* a result of a form submission (if the expression ``'form.submitted' in request.params`` is ``False``), then the view renders the edit form, passing the page resource, and a ``save_url`` which will be used as the action of the generated form.
+
+If the view execution *is* a result of a form submission (if the expression ``'form.submitted' in request.params`` is ``True``), the view grabs the ``body`` element of the request parameter and sets it as the ``data`` attribute of the page context.
+It then redirects to the default view of the context (the page), which will always be the ``view_page`` view.
+
+
+Modifying the ``notfound_view`` in ``notfound.py``
+--------------------------------------------------
+
+We have one more view to modify.
+Open ``tutorial/views/notfound.py`` and make the changes shown by the emphasized lines.
+
+.. literalinclude:: src/views/tutorial/views/notfound.py
+ :linenos:
+ :language: python
+ :emphasize-lines: 3-4, 9-12
+
+We need to import the ``Page`` from our models.
+We eventually return a ``Page`` object as ``page`` into the template ``layout.pt`` to display its name in the title tag.
+
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.
+The ``view_page``, ``add_page``, and ``edit_page`` views that we 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.
+
+
+The ``layout.pt`` template
+--------------------------
+
+Update ``tutorial/templates/layout.pt`` with the following content, as indicated by the emphasized lines:
+
+.. literalinclude:: src/views/tutorial/templates/layout.pt
+ :linenos:
+ :emphasize-lines: 11-12, 37-41
+ :language: html
+
+Since we are using a templating engine, we can factor common boilerplate out of our page templates into reusable components.
+We can do this via :term:`METAL` macros and slots.
+
+- The cookiecutter defined a macro named ``layout`` (line 1).
+ This macro consists of the entire template.
+- We changed the ``title`` tag to use the ``name`` attribute of a ``page`` object, or if it does not exist then the page title (lines 11-12).
+- The cookiecutter defined a macro customization point or `slot` (line 36).
+ This slot is inside the macro ``layout``.
+ Therefore it can be replaced by content, customizing the macro.
+- We added a ``div`` element with a link to allow the user to return to the front page (lines 37-41).
+- We removed the row of icons and links from the original cookiecutter.
+
+.. seealso::
+
+ Please refer to the Chameleon documentation for more information about using `METAL <https://chameleon.readthedocs.io/en/latest/>`_ for defining and using macros and slots.
+
The ``view.pt`` template
------------------------
@@ -318,16 +310,18 @@ Rename ``tutorial/templates/mytemplate.pt`` to ``tutorial/templates/view.pt`` an
.. literalinclude:: src/views/tutorial/templates/view.pt
:linenos:
:language: html
- :emphasize-lines: 11-12,37-52
+ :emphasize-lines: 5-16
This template is used by ``view_page()`` for displaying a single
wiki page. It includes:
-- A ``div`` element that is replaced with the ``content`` value provided by
- the view (lines 37-39). ``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 41-43).
+- The use of a macro to load the entire template ``layout.pt``.
+- The template fills the slot named ``content`` (line 2) with a ``div`` element.
+- A ``div`` element that is replaced with the ``page_text`` value provided by the view (line 5).
+ ``page_text`` contains HTML, so the ``structure`` keyword is used to prevent escaping HTML entities, such as changing ``>`` to ``&gt;``.
+- A link that points at the "edit" URL, which invokes the ``edit_page`` view for the page being viewed (lines 9-11).
+- A ``span`` whose content is replaced by the name of the page, if present.
+
The ``edit.pt`` template
------------------------
@@ -337,59 +331,52 @@ Copy ``tutorial/templates/view.pt`` to ``tutorial/templates/edit.pt`` and edit t
.. literalinclude:: src/views/tutorial/templates/edit.pt
:linenos:
:language: html
+ :emphasize-lines: 5-20
-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 ``add_page()`` and ``edit_page()`` for adding and editing a wiki page.
+It displays a page containing a form that includes:
-- A 10-row by 60-column ``textarea`` field named ``body`` that is filled
- with any existing page data when it is rendered (line 46).
-- A submit button that has the name ``form.submitted`` (line 49).
+- A 10-row by 60-column ``textarea`` field named ``body`` that is filled with any existing page data when it is rendered (lines 11-13).
+- A submit button that has the name ``form.submitted`` (lines 16-18).
-The form POSTs back to the ``save_url`` argument supplied by the view (line
-44). The view will use the ``body`` and ``form.submitted`` values.
+When submitted, the form sends a POST request to the ``save_url`` argument supplied by the view (line 9).
+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.
+.. 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.
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.
+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 by the cookiecutter 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 ``__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.
+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 in the ``routes.py`` file.
+Any number and type of static assets can be placed in this directory (or subdirectories)
+They are referred to by either URL or using the convenience method ``static_url``, for example ``request.static_url('<package>:static/foo.css')``, within templates.
Viewing the application in a browser
====================================
-We can finally examine our application in a browser (See
-:ref:`wiki-start-the-application`). Launch a browser and visit
-each of the following URLs, checking that the result is as expected:
+We can finally examine our application in a browser (See :ref:`wiki-start-the-application`).
+Launch a browser and visit 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`` resource.
-- http://localhost:6543/ invokes the ``view_wiki`` view. This always
- redirects to the ``view_page`` view of the ``FrontPage`` Page resource.
+- http://localhost:6543/FrontPage/ invokes the ``view_page`` view of the front page resource.
+ This is because it is the :term:`default view` (a view without a ``name``) for ``Page`` resources.
-- http://localhost:6543/FrontPage/ invokes the ``view_page`` view of the front
- page resource. This is because it's the :term:`default view` (a view
- without a ``name``) for Page resources.
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the ``FrontPage`` ``Page`` resource.
-- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
- ``FrontPage`` Page resource.
+- http://localhost:6543/add_page/SomePageName invokes the add view for a ``Page``.
-- http://localhost:6543/add_page/SomePageName invokes the add view for a Page.
+- To generate an error, visit http://localhost:6543/add_page which will generate an ``IndexError: tuple index out of range`` error.
+ You will see an interactive traceback facility provided by :term:`pyramid_debugtoolbar`.
-- To generate an error, visit http://localhost:6543/add_page which will
- generate an ``IndexError: tuple index out of range`` error. You'll see an
- interactive traceback facility provided by :term:`pyramid_debugtoolbar`.
+- To generate a not found error, visit http://localhost:6543/wakawaka which will invoke the ``notfound_view`` view provided by the cookiecutter.