summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki/definingviews.rst
diff options
context:
space:
mode:
authorChristoph Zwerschke <cito@online.de>2016-04-19 20:07:12 +0200
committerChristoph Zwerschke <cito@online.de>2016-04-19 20:07:12 +0200
commit3629c49e46207ff5162a82883c14937e6ef4c186 (patch)
tree1306181202cb8313f16080789f5b9ab1eeb61d53 /docs/tutorials/wiki/definingviews.rst
parent804ba0b2f434781e77d2b5191f1cd76a490f6610 (diff)
parent6c16fb020027fac47e4d2e335cd9e264dba8aa3b (diff)
downloadpyramid-3629c49e46207ff5162a82883c14937e6ef4c186.tar.gz
pyramid-3629c49e46207ff5162a82883c14937e6ef4c186.tar.bz2
pyramid-3629c49e46207ff5162a82883c14937e6ef4c186.zip
Merge remote-tracking branch 'refs/remotes/Pylons/master'
Diffstat (limited to 'docs/tutorials/wiki/definingviews.rst')
-rw-r--r--docs/tutorials/wiki/definingviews.rst356
1 files changed, 217 insertions, 139 deletions
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index ae4fa6ffb..ac94d8059 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -1,3 +1,5 @@
+.. _wiki_defining_views:
+
==============
Defining Views
==============
@@ -7,7 +9,9 @@ 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
+.. 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
@@ -15,7 +19,7 @@ assumed to return a :term:`response` object.
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
+ "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
@@ -26,45 +30,91 @@ assumed to return a :term:`response` object.
We're going to define several :term:`view callable` functions, then wire them
into :app:`Pyramid` using some :term:`view configuration`.
-The source code for this tutorial stage can be browsed via
-`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/views/
-<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/views/>`_.
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 ``paster create`` command; it doesn't 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 ``install_requires`` parameter in the
-``setup`` function.
+application was generated by the ``pcreate`` command; it doesn't know
+about our custom application requirements.
-Our resulting ``setup.py`` should look like so:
+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:
.. literalinclude:: src/views/setup.py
:linenos:
+ :emphasize-lines: 20
:language: python
-.. note:: After these new dependencies are added, you will need to
- rerun ``python setup.py develop`` inside the root of the
- ``tutorial`` package to obtain and register the newly added
- dependency package.
+Only the highlighted line needs to be added.
+
+
+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.
+
+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:
+
+.. code-block:: bash
+
+ $ cd tutorial
+ $ $VENV/bin/pip install -e .
+
+On Windows:
+
+.. code-block:: doscon
+
+ c:\pyramidtut> cd tutorial
+ c:\pyramidtut\tutorial> %VENV%\Scripts\pip install -e .
+
+Success executing this command will end with a line to the console something
+like:
+
+.. code-block:: text
+
+ Successfully installed docutils-0.12 tutorial-0.0
-Adding View Functions
-=====================
-We're going to add four :term:`view callable` functions to our ``views.py``
-module. One view named ``view_wiki`` will display the wiki itself (it will
-answer on the root URL), another named ``view_page`` will display an
-individual page, another named ``add_page`` will allow a page to be added,
-and a final view named ``edit_page`` will allow a page to be edited.
+Adding view functions in ``views.py``
+=====================================
+
+It's time for a major change. Open ``tutorial/views.py`` and edit it to look
+like the following:
+
+.. literalinclude:: src/views/tutorial/views.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 we originally rendered the ``zodb`` scaffold. It was only an example and
+isn't relevant to our application.
+
+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.
.. note::
There is nothing special about the filename ``views.py``. A project may
- have many view callables throughout its codebase in arbitrarily-named
+ 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.
@@ -72,45 +122,72 @@ and a final view named ``edit_page`` will allow a page to be edited.
The ``view_wiki`` view function
-------------------------------
-The ``view_wiki`` function will be configured to respond as the default view
-callable for a Wiki resource. We'll 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, 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
+Following is the code for the ``view_wiki`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 12-14
+ :lineno-start: 12
+ :linenos:
+ :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 WebOb :term:`response` interface). The :func:`pyramid.url.resource_url`
-API. :func:`pyramid.url.resource_url` constructs a URL to the ``FrontPage``
-page resource (e.g. ``http://localhost:6543/FrontPage``), and uses it as the
-"location" of the HTTPFound response, forming an HTTP redirect.
+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.
The ``view_page`` view function
-------------------------------
-The ``view_page`` function will be configured to respond as the default view
-of a Page resource. We'll provide it with a ``@view_config`` decorator which
+Here is the code for the ``view_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 16-33
+ :lineno-start: 16
+ :linenos:
+ :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
+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.
+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 with the
+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.
@@ -118,8 +195,8 @@ 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
+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
@@ -138,15 +215,23 @@ callable. In the ``view_wiki`` view callable, we unconditionally return a
The ``add_page`` view function
------------------------------
-The ``add_page`` function will be configured to respond when the context
-resource is a Wiki and the :term:`view name` is ``add_page``. We'll 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``
+Here is the code for the ``add_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 35-50
+ :lineno-start: 35
+ :linenos:
+ :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
+: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
@@ -159,7 +244,7 @@ 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``,
+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 zeroth element of the subpath (the wiki page name),
@@ -169,14 +254,14 @@ 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
-use as the form post URL during rendering. We're lazy here, so we're trying
+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 scrape the page body
+``'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
@@ -185,8 +270,16 @@ the page body, and save it into "our context" (the Wiki) using the
The ``edit_page`` view function
-------------------------------
-The ``edit_page`` function will be configured to respond when the context is
-a Page resource and the :term:`view name` is ``edit_page``. We'll provide it
+Here is the code for the ``edit_page`` view function and its decorator:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :lines: 52-60
+ :lineno-start: 52
+ :linenos:
+ :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
@@ -211,110 +304,95 @@ If the view execution *is* a result of a form submission (if the expression
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.
-Viewing the Result of all Our Edits to ``views.py``
-===================================================
+Adding templates
+================
-The result of all of our edits to ``views.py`` will leave it looking like
-this:
+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.
-.. literalinclude:: src/views/tutorial/views.py
- :linenos:
- :language: python
+The ``view.pt`` template
+------------------------
-Adding Templates
-================
+Create ``tutorial/templates/view.pt`` and add the following
+content:
+
+.. literalinclude:: src/views/tutorial/templates/view.pt
+ :linenos:
+ :language: html
-Most view callables we've added expected to be rendered via a
-:term:`template`. The default templating systems in :app:`Pyramid` are
-:term:`Chameleon` and :term:`Mako`. Chameleon is a variant of :term:`ZPT`,
-which is an XML-based templating language. Mako is a non-XML-based
-templating language. Because we had to pick one, we chose Chameleon for this
-tutorial.
+This template is used by ``view_page()`` for displaying a single
+wiki page. It includes:
-The templates we create will live in the ``templates`` directory of our
-tutorial package. Chameleon templates must have a ``.pt`` extension to be
-recognized as such.
+- 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).
-The ``view.pt`` Template
+The ``edit.pt`` template
------------------------
-The ``view.pt`` template is used for viewing a single Page. It is used by
-the ``view_page`` view function. It should have a div that is "structure
-replaced" with the ``content`` value provided by the view. It should also
-have a link on the rendered page that points at the "edit" URL (the URL which
-invokes the ``edit_page`` view for the page being viewed).
+Create ``tutorial/templates/edit.pt`` and add the following content:
-Once we're done with the ``view.pt`` template, it will look a lot like
-the below:
+.. literalinclude:: src/views/tutorial/templates/edit.pt
+ :linenos:
+ :language: html
-.. literalinclude:: src/views/tutorial/templates/view.pt
- :language: xml
-
-.. note:: The names available for our use in a template are always those that
- are present in the dictionary returned by the view callable. But our
- templates make use of a ``request`` object that none of our tutorial views
- return in their dictionary. This value appears as if "by magic".
- However, ``request`` is one of several names that are available "by
- default" in a template when a template renderer is used. See
- :ref:`chameleon_template_renderers` for more information about other names
- that are available by default in a template when a template is used as a
- renderer.
-
-The ``edit.pt`` Template
-------------------------
+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:
-The ``edit.pt`` template is used for adding and editing a Page. It is used
-by the ``add_page`` and ``edit_page`` view functions. It should display a
-page containing a form that POSTs back to the "save_url" argument supplied by
-the view. The form should have a "body" textarea field (the page data), and
-a submit button that has the name "form.submitted". The textarea in the form
-should be filled with any existing page data when it is rendered.
+- 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).
-Once we're done with the ``edit.pt`` template, it will look a lot like the
-below:
+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.
-.. literalinclude:: src/views/tutorial/templates/edit.pt
- :language: xml
+.. 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
+
+Static assets
-------------
-Our templates name a single static asset named ``pylons.css``. We don't need
-to create this file within our package's ``static`` directory because it was
-provided at the time we created the project. This file is a little too long to
-replicate within the body of this guide, however it is available `online
-<http://github.com/Pylons/pyramid/blob/master/docs/tutorials/wiki/src/views/tutorial/static/pylons.css>`_.
+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.
-This CSS file will be accessed via
-e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to
+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.
+method ``static_url``, e.g.,
+``request.static_url('<package>:static/foo.css')`` within templates.
+
-Viewing the Application in a Browser
+Viewing the application in a browser
====================================
-We can finally examine our application in a
-browser. The views we'll try are as follows:
+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:
-- Visiting ``http://localhost:6543/`` in a browser 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.
-- Visiting ``http://localhost:6543/FrontPage/`` in a browser invokes
- the ``view_page`` view of the front page resource. This is
- because it's the *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.
-- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser
- invokes the edit view for the ``FrontPage`` Page resource.
+- http://localhost:6543/FrontPage/edit_page invokes the edit view for the
+ ``FrontPage`` Page resource.
-- Visiting ``http://localhost:6543/add_page/SomePageName`` in a
- browser 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`` for the expression
- ``request.subpath[0]``. You'll see an interactive traceback
- facility provided by :term:`WebError`.
+- To generate an error, visit http://localhost:6543/add_page which will
+ generate an ``IndexErrorr: tuple index out of range`` error. You'll see an
+ interactive traceback facility provided by :term:`pyramid_debugtoolbar`.