summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/tutorials/wiki2/definingviews.rst274
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja26
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja27
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja24
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views/default.py36
5 files changed, 185 insertions, 142 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 "&gt;").
- 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
====================================
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
index e47b3aabf..7db25c674 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
@@ -1,17 +1,17 @@
{% extends 'layout.jinja2' %}
-{% block title %}Edit {{page.name}} - {% endblock title %}
+{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %}
{% block content %}
<p>
-Editing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
+Editing <strong>{{pagename}}</strong>
</p>
<p>You can return to the
<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
<form action="{{ save_url }}" method="post">
<div class="form-group">
- <textarea class="form-control" name="body" rows="10" cols="60">{{ page.data }}</textarea>
+ <textarea class="form-control" name="body" rows="10" cols="60">{{ pagedata }}</textarea>
</div>
<div class="form-group">
<button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2
index 82a144abf..71785157f 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2
@@ -8,7 +8,7 @@
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="{{request.static_url('tutorial:static/pyramid-16x16.png')}}">
- <title>{% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
+ <title>{% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
@@ -33,11 +33,6 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if request.authenticated_userid is not none %}
- <p class="pull-right">
- <a href="{{ request.route_url('logout') }}">Logout</a>
- </p>
- {% endif %}
{% block content %}{% endblock %}
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
index c582ce1f9..94419e228 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
@@ -1,5 +1,7 @@
{% extends 'layout.jinja2' %}
+{% block subtitle %}{{page.name}} - {% endblock subtitle %}
+
{% block content %}
<p>{{ content|safe }}</p>
<p>
@@ -8,7 +10,7 @@
</a>
</p>
<p>
- Viewing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
+ Viewing <strong>{{page.name}}</strong>, created by <strong>{{page.creator.name}}</strong>.
</p>
<p>You can return to the
<a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
index ca37f39f5..7a4073b3f 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
@@ -9,7 +9,7 @@ from pyramid.httpexceptions import (
from pyramid.view import view_config
-from ..models import Page
+from ..models import Page, User
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
@@ -26,7 +26,7 @@ def view_page(request):
if page is None:
raise HTTPNotFound('No such page')
- def check(match):
+ def add_link(match):
word = match.group(1)
exists = request.dbsession.query(Page).filter_by(name=word).all()
if exists:
@@ -37,23 +37,10 @@ def view_page(request):
return '<a href="%s">%s</a>' % (add_url, cgi.escape(word))
content = publish_parts(page.data, writer_name='html')['html_body']
- content = wikiwords.sub(check, content)
+ content = wikiwords.sub(add_link, content)
edit_url = request.route_url('edit_page', pagename=pagename)
return dict(page=page, content=content, edit_url=edit_url)
-@view_config(route_name='add_page', renderer='../templates/edit.jinja2')
-def add_page(request):
- pagename = request.matchdict['pagename']
- if 'form.submitted' in request.params:
- body = request.params['body']
- page = Page(name=pagename, data=body)
- request.dbsession.add(page)
- next_url = request.route_url('view_page', pagename=pagename)
- return HTTPFound(location=next_url)
- save_url = request.route_url('add_page', pagename=pagename)
- page = Page(name='', data='')
- return dict(page=page, save_url=save_url)
-
@view_config(route_name='edit_page', renderer='../templates/edit.jinja2')
def edit_page(request):
pagename = request.matchdict['pagename']
@@ -63,6 +50,21 @@ def edit_page(request):
next_url = request.route_url('view_page', pagename=pagename)
return HTTPFound(location=next_url)
return dict(
- page=page,
+ pagename=page.name,
+ pagedata=page.data,
save_url=request.route_url('edit_page', pagename=pagename),
)
+
+@view_config(route_name='add_page', renderer='../templates/edit.jinja2')
+def add_page(request):
+ pagename = request.matchdict['pagename']
+ if 'form.submitted' in request.params:
+ body = request.params['body']
+ page = Page(name=pagename, data=body)
+ page.creator = (
+ request.dbsession.query(User).filter_by(name='editor').one())
+ request.dbsession.add(page)
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
+ save_url = request.route_url('add_page', pagename=pagename)
+ return dict(pagename=pagename, pagedata='', save_url=save_url)