diff options
| author | Christoph Zwerschke <cito@online.de> | 2016-04-19 20:07:12 +0200 |
|---|---|---|
| committer | Christoph Zwerschke <cito@online.de> | 2016-04-19 20:07:12 +0200 |
| commit | 3629c49e46207ff5162a82883c14937e6ef4c186 (patch) | |
| tree | 1306181202cb8313f16080789f5b9ab1eeb61d53 /docs/tutorials/wiki/definingviews.rst | |
| parent | 804ba0b2f434781e77d2b5191f1cd76a490f6610 (diff) | |
| parent | 6c16fb020027fac47e4d2e335cd9e264dba8aa3b (diff) | |
| download | pyramid-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.rst | 356 |
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 ">", 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`. |
