diff options
| author | Michael Merickel <michael@merickel.org> | 2016-02-13 13:26:05 -0600 |
|---|---|---|
| committer | Michael Merickel <michael@merickel.org> | 2016-02-13 13:26:05 -0600 |
| commit | 60891b844c883d2c9ce864522f2202d9514d8d83 (patch) | |
| tree | 492c18bf615c1e5023ca79896c0b8a780120b1e8 /docs/tutorials/wiki2/definingviews.rst | |
| parent | 23c2d7b337a5873dba0ca6c146e1174136ac2187 (diff) | |
| download | pyramid-60891b844c883d2c9ce864522f2202d9514d8d83.tar.gz pyramid-60891b844c883d2c9ce864522f2202d9514d8d83.tar.bz2 pyramid-60891b844c883d2c9ce864522f2202d9514d8d83.zip | |
improve the views section by removing quirks and explaining transactions
Diffstat (limited to 'docs/tutorials/wiki2/definingviews.rst')
| -rw-r--r-- | docs/tutorials/wiki2/definingviews.rst | 274 |
1 files changed, 159 insertions, 115 deletions
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 8bccc3fc0..8f0f7b51d 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -37,6 +37,80 @@ Only the highlighted line needs to be added. 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 ``__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`` +================================ + +This is the URL Dispatch tutorial and so let's start by adding some +URL patterns to our app. Later we'll attach views to handle the URLs. + +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'``. + +#. 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'``. + +#. 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'``. + +As a result of our edits, the ``__init__.py`` file should look +something like: + +.. literalinclude:: src/views/tutorial/__init__.py + :linenos: + :emphasize-lines: 11-14 + :language: python + +The highlighted lines are the ones that need to be added or edited. + +.. warn:: + + 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 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. + Adding view functions in ``views/default.py`` ============================================= @@ -46,7 +120,7 @@ edit it to look like the following: .. literalinclude:: src/views/tutorial/views/default.py :linenos: :language: python - :emphasize-lines: 1-9,12-68 + :emphasize-lines: 1-9,12-70 The highlighted lines need to be added or edited. @@ -54,7 +128,7 @@ 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. We also delated the ``db_err_msg`` +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/default.py`` @@ -88,7 +162,7 @@ Following is the code for the ``view_wiki`` view function and its decorator: :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 @@ -96,7 +170,7 @@ 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 +: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. @@ -116,12 +190,12 @@ 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 @@ -136,19 +210,73 @@ associated with the view configuration to render a response. In our case, the renderer used will be the ``view.jinja2`` template, as indicated in the ``@view_config`` decorator that is applied to ``view_page()``. +If the page does not exist then we need to handle that by raising +:class:`pyramid.httpexceptions.HTTPNotFound`` to trigger our 404 handling +defined in ``tutorial/views/notfound.py``. + +.. note:: + + 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 probably the problem! See + :ref:`special_exceptions_in_callables` for more information about + exception views. + +The ``edit_page`` view function +------------------------------- + +Here is the code for the ``edit_page`` view function and its decorator: + +.. 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. + +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 +``body`` element of the request parameters and sets it as the ``data`` +attribute of the page object. It then redirects to the ``view_page`` view +of the wiki 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 +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: 44-55 + :lines: 58-70 :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 ``check`` function +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 @@ -164,8 +292,12 @@ 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``. We then redirect back to the ``view_page`` view for -the newly created page. +``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 so we query 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 @@ -177,34 +309,6 @@ 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/default.py - :lines: 57-68 - :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. - -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 -``body`` element of the request parameters and sets it as the ``data`` -attribute of the page object. It then redirects to the ``view_page`` view -of the wiki 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 -simply renders the edit form, passing the page object and a ``save_url`` -which will be used as the action of the generated form. - Adding templates ================ @@ -229,7 +333,7 @@ our page templates into reusable components. One method for doing this is template inheritance via blocks. - We have defined 2 placeholders in the layout template where a child template - can override the content. These blocks are named ``title`` (line 11) and + 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. @@ -237,44 +341,45 @@ is template inheritance via blocks. The ``view.jinja2`` template ---------------------------- -Create ``tutorial/templates/view.jinja2`` and add the following -content: +Create ``tutorial/templates/view.jinja2`` and add the following content: .. literalinclude:: src/views/tutorial/templates/view.jinja2 :linenos: :emphasize-lines: 1,4,6-8 :language: html -This template is used by ``view_page()`` for displaying a single -wiki page. It includes: +This template is used by ``view_page()`` for displaying a single wiki page. +It includes: - 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 to insert the + page name of the page into the page's title (line 3). - We override the ``content`` block from the base layout to insert our markup - into the body (line 3). + into the body (line 5-18). - A variable that is replaced with the ``content`` value provided by the view - (line 4). ``content`` contains HTML, so the ``|safe`` filter is used to + (line 6). ``content`` contains HTML, so the ``|safe`` filter is used to prevent escaping it (e.g., changing ">" to ">"). - A link that points at the "edit" URL which invokes the ``edit_page`` view for - the page being viewed (lines 6-8). + the page being viewed (line 9). The ``edit.jinja2`` template ---------------------------- -Create ``tutorial/templates/edit.jinja2`` and add the following -content: +Create ``tutorial/templates/edit.jinja2`` and add the following content: .. literalinclude:: src/views/tutorial/templates/edit.jinja2 :linenos: :emphasize-lines: 3,12,14,17 :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 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 that includes: -- Again we are extending the ``layout.jinja2`` template which provides - the skeleton of the page. -- Override the ``title`` block to affect the ``<title>`` tag in the +- Again, we are extending 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). @@ -326,67 +431,6 @@ our own templates for the wiki. 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. - -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'``. - -#. 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'``. - -#. 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'``. - -As a result of our edits, the ``__init__.py`` file should look -something like: - -.. literalinclude:: src/views/tutorial/__init__.py - :linenos: - :emphasize-lines: 11-14 - :language: python - -The highlighted lines are the ones that need to be added or edited. - Viewing the application in a browser ==================================== |
