diff options
| author | Chris McDonough <chrism@plope.com> | 2010-10-25 17:58:23 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2010-10-25 17:58:23 -0400 |
| commit | c426c49cd433997aac10045052d77d9c749680b2 (patch) | |
| tree | 4dc3c7664ad9492b80771eb9c55fa0d2bfe22162 /docs/tutorials/bfgwiki2/definingviews.rst | |
| parent | e26700528995b1807c5e55a4295a6f788a5603de (diff) | |
| download | pyramid-c426c49cd433997aac10045052d77d9c749680b2.tar.gz pyramid-c426c49cd433997aac10045052d77d9c749680b2.tar.bz2 pyramid-c426c49cd433997aac10045052d77d9c749680b2.zip | |
remove bfgwiki2 old dir
Diffstat (limited to 'docs/tutorials/bfgwiki2/definingviews.rst')
| -rw-r--r-- | docs/tutorials/bfgwiki2/definingviews.rst | 402 |
1 files changed, 0 insertions, 402 deletions
diff --git a/docs/tutorials/bfgwiki2/definingviews.rst b/docs/tutorials/bfgwiki2/definingviews.rst deleted file mode 100644 index c33795883..000000000 --- a/docs/tutorials/bfgwiki2/definingviews.rst +++ /dev/null @@ -1,402 +0,0 @@ -============== -Defining Views -============== - -A :term:`view callable` in a :term:`url dispatch` -based -:mod:`repoze.bfg` application is typically a simple Python function -that accepts a single parameter named :term:`request`. A view -callable is assumed to return a :term:`response` object. - -.. note:: A :mod:`repoze.bfg` view can also be defined as callable - which accepts *two* arguments: a :term:`context` and a - :term:`request`. You'll see this two-argument pattern used in - other :mod:`repoze.bfg` tutorials and applications. Either calling - convention will work in any :mod:`repoze.bfg` application; the - calling conventions can be used interchangeably as necessary. In - :term:`url dispatch` based applications, however, the context - object is rarely used in the view body itself, so within this - tutorial we define views as callables that accept only a request to - avoid the visual "noise". If you do need the ``context`` within a - view function that only takes the request as a single argument, you - can obtain it via ``request.context``. - -The request passed to every view that is called as the result of a -route match has an attribute named ``matchdict`` that contains the -elements placed into the URL by the ``pattern`` of a ``route`` -statement. For instance, if a route statement in ``configure.zcml`` -had the pattern ``:one/:two``, and the URL at -``http://example.com/foo/bar`` was invoked, matching this pattern, the -matchdict dictionary attached to the request passed to the view would -have a ``one`` key with the value ``foo`` and a ``two`` key with the -value ``bar``. - -The source code for this tutorial stage can be browsed at -`docs.repoze.org <http://docs.repoze.org/bfgwiki2-1.3/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. - -Our resulting ``setup.py`` should look like so: - -.. literalinclude:: src/views/setup.py - :linenos: - :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. - -Adding View Functions -===================== - -We'll get rid of our ``my_view`` view function in our ``views.py`` -file. It's only an example and isn't relevant to our application. - -Then we're going to add four :term:`view callable` functions to our -``views.py`` module. One view callable (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 callable -named ``edit_page`` will allow a page to be edited. We'll describe -each one briefly and show the resulting ``views.py`` file afterward. - -.. note:: - - There is nothing special about the filename ``views.py``. A project - may have many view callables throughout its codebase in - arbitrarily-named files. Files implementing view callables often - have ``view`` in their filenames (or may live in a Python subpackage - of your application package named ``views``), but this is only by - convention. - -The ``view_wiki`` view function -------------------------------- - -The ``view_wiki`` function will respond as the :term:`default view` of -a ``Wiki`` model object. It always redirects to a URL which -represents the path to our "FrontPage". It returns an instance of the -:class:`webob.exc.HTTPFound` class (instances of which implement the -WebOb :term:`response` interface), It will use the -:func:`repoze.bfg.url.route_url` API to construct a URL to the -``FrontPage`` page (e.g. ``http://localhost:6543/FrontPage``), and -will use it as the "location" of the HTTPFound response, forming an -HTTP redirect. - -The ``view_page`` view function -------------------------------- - -The ``view_page`` function will respond as the :term:`default view` of -a ``Page`` object. The ``view_page`` function renders the -:term:`ReStructuredText` body of a page (stored as the ``data`` -attribute of a Page 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 -``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, 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 matched WikiWord name, the function generates an "add" link as the -substitution value and returns it. - -As a result, the ``content`` variable is now a fully formed bit of -HTML containing various view and add links for WikiWords based on the -content of our current page object. - -We then generate an edit URL (because it's easier to do here than in -the template), and we return a dictionary with a number of arguments. -The fact that this view returns a dictionary (as opposed to a -:term:`response` object) is a cue to :mod:`repoze.bfg` that it should -try to use a :term:`renderer` associated with the view configuration -to render a template. In our case, the template which will be -rendered will be the ``templates/view.pt`` template, as per the -configuration put into effect in ``configure.zcml``. - -The ``add_page`` view function ------------------------------- - -The ``add_page`` function will be invoked when a user clicks on a -*WikiWord* which isn't yet represented as a page in the system. The -``check`` function within the ``view_page`` view generates URLs to -this view. It also acts as a handler for the form that is generated -when we want to add a page object. The ``matchdict`` attribute of the -request passed to the ``add_page`` view will have the values we need -to construct URLs and find model objects. - -The matchdict will have a ``pagename`` key that matches the name of -the page we'd like to add. If our add view is invoked via, -e.g. ``http://localhost:6543/add_page/SomeName``, the ``pagename`` -value in the matchdict will be ``SomeName``. - -If the view execution is *not* a result of a form submission (if the -expression ``'form.submitted' in request.params`` is ``False``), the -view callable 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 to use the same template -(``templates/edit.pt``) for the add view as well as the page edit -view, so we create a dummy Page object in order to satisfy the edit -form's desire to have *some* page object exposed as ``page``, and -:mod:`repoze.bfg` will render the template associated with this view -to a response. - -If the view execution *is* a result of a form submission (if the -expression ``'form.submitted' in request.params`` is ``True``), we -scrape the page body from the form data, create a Page object using -the name in the matchdict ``pagename``, and obtain the page body from -the request, and save it into the database using ``session.add``. We -then redirect back to the ``view_page`` view (the :term:`default view` -for a Page) for the newly created page. - -The ``edit_page`` view function -------------------------------- - -The ``edit_page`` function will be invoked when a user clicks the -"Edit this Page" button on the view form. It renders an edit form but -it also acts as the handler for the form it renders. The -``matchdict`` attribute of the request passed to the ``add_page`` view -will have a ``pagename`` key matching the name of the page the user -wants to edit. - -If the view execution is *not* a result of a form submission (if the -expression ``'form.submitted' in request.params`` is ``False``), the -view simply renders the edit form, passing the request, the page -object, and a save_url which will be used as the action of the -generated form. - -If the view execution *is* a result of a form submission (if the -expression ``'form.submitted' in request.params`` is ``True``), the -view grabs the ``body`` element of the request parameter and sets it -as the ``data`` key in the matchdict. It then redirects to the -default view of the wiki page, which will always be the ``view_page`` -view. - -Viewing the Result of Our Edits to ``views.py`` -=============================================== - -The result of all of our edits to ``views.py`` will leave it looking -like this: - -.. literalinclude:: src/views/tutorial/views.py - :linenos: - :language: python - -Adding Templates -================ - -The views we've added all reference a :term:`template`. Each template -is a :term:`Chameleon` template. The default templating system in -:mod:`repoze.bfg` is a variant of :term:`ZPT` provided by -:term:`Chameleon`. These templates will live in the ``templates`` -directory of our tutorial package. - -The ``view.pt`` Template ------------------------- - -The ``view.pt`` template is used for viewing a single wiki 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). - -Once we're done with the ``view.pt`` template, it will look a lot like -the below: - -.. literalinclude:: src/views/tutorial/templates/view.pt - :linenos: - :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 Chameleon template is used as a renderer. - -The ``edit.pt`` Template ------------------------- - -The ``edit.pt`` template is used for adding and editing a wiki 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. - -Once we're done with the ``edit.pt`` template, it will look a lot like -the below: - -.. literalinclude:: src/views/tutorial/templates/edit.pt - :linenos: - :language: xml - -Static Resources ----------------- - -Our templates name a single static resource named ``style.css``. We -need to create this and place it in a file named ``style.css`` within -our package's ``templates/static`` directory. This file is a little -too long to replicate within the body of this guide, however it is -available `online -<http://docs.repoze.org/bfgwiki2-1.2/views/tutorial/templates/static/style.css>`_. - -This CSS file will be accessed via -e.g. ``http://localhost:6543/static/style.css`` by virtue of the -``<static>`` directive we've defined in the ``configure.zcml`` file. -Any number and type of static resources can be placed in this -directory (or subdirectories) and are just referred to by URL within -templates. - -Mapping Views to URLs in ``configure.zcml`` -=========================================== - -The ``configure.zcml`` file contains ``route`` declarations (and a -lone ``view`` declaration) which serve to map URLs via :term:`url -dispatch` to view functions. 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 ``route`` declarations to ``configure.zcml``. -Note that the *ordering* of these declarations is very important. -``route`` declarations are matched in the order they're found in the -``configure.zcml`` file. - -#. Add a declaration which maps the empty pattern (signifying the root - URL) to the view named ``view_wiki`` in our ``views.py`` file with - the name ``view_wiki``. This is the :term:`default view` for the - wiki. - -#. Add a declaration which maps the pattern ``:pagename`` to the - view named ``view_page`` in our ``views.py`` file with the view - name ``view_page``. This is the regular view for a page. - -#. Add a declaration which maps the pattern - ``:pagename/edit_page`` to the view named ``edit_page`` in our - ``views.py`` file with the name ``edit_page``. This is the edit view - for a page. - -#. Add a declaration which maps the pattern - ``add_page/:pagename`` to the view named ``add_page`` in our - ``views.py`` file with the name ``add_page``. This is the add view - for a new page. - -As a result of our edits, the ``configure.zcml`` file should look -something like so: - -.. literalinclude:: src/views/tutorial/configure.zcml - :linenos: - :language: xml - -The WSGI Pipeline ------------------ - -Within ``tutorial.ini``, note the existence of a ``[pipeline:main]`` -section which specifies our WSGI pipeline. This "pipeline" will be -served up as our WSGI application. As far as the WSGI server is -concerned the pipeline *is* our application. Simpler configurations -don't use a pipeline: instead they expose a single WSGI application as -"main". Our setup is more complicated, so we use a pipeline. - -``egg:repoze.tm2#tm`` is at the "top" of the pipeline. This is a -piece of middleware which commits a transaction if no exception -occurs; if an exception occurs, the transaction will be aborted. This -is the piece of software that allows us to forget about needing to do -manual commits and aborts of our database connection in view code. - -Adding an Element to the Pipeline ---------------------------------- - -Let's add a piece of middleware to the WSGI pipeline. We'll add -``egg:Paste#evalerror`` middleware which displays debuggable errors in -the browser while you're developing (this is *not* recommended for -deployment as it is a security risk). Let's insert evalerror into the -pipeline right above ``egg:repoze.tm2#tm``, making our resulting -``tutorial.ini`` file look like so: - -.. literalinclude:: src/views/tutorial.ini - :linenos: - :language: ini - -Viewing the Application in a Browser -==================================== - -Once we've set up the WSGI pipeline properly, we can finally examine -our application in a browser. The views we'll try are as follows: - -- Visiting ``http://localhost:6543`` in a browser invokes the - ``view_wiki`` view. This always redirects to the ``view_page`` view - of the FrontPage page object. - -- Visiting ``http://localhost:6543/FrontPage`` in a browser invokes - the ``view_page`` view of the front page page object. - -- Visiting ``http://localhost:6543/FrontPage/edit_page`` in a browser - invokes the edit view for the front page object. - -- Visiting ``http://localhost:6543/add_page/SomePageName`` in a - browser invokes the add view for a page. - -Try generating an error within the body of a view by adding code to -the top of it that generates an exception (e.g. ``raise -Exception('Forced Exception')``). Then visit the error-raising view -in a browser. You should see an interactive exception handler in the -browser which allows you to examine values in a post-mortem mode. - -Adding Tests -============ - -Since we've added a good bit of imperative code here, it's useful to -define tests for the views we've created. We'll change our tests.py -module to look like this: - -.. literalinclude:: src/views/tutorial/tests.py - :linenos: - :language: python - -We can then run the tests using something like: - -.. code-block:: text - :linenos: - - $ python setup.py test -q - -The expected output is something like: - -.. code-block:: text - :linenos: - - running test - running egg_info - writing requirements to tutorial.egg-info/requires.txt - writing tutorial.egg-info/PKG-INFO - writing top-level names to tutorial.egg-info/top_level.txt - writing dependency_links to tutorial.egg-info/dependency_links.txt - writing entry points to tutorial.egg-info/entry_points.txt - unrecognized .svn/entries format in - reading manifest file 'tutorial.egg-info/SOURCES.txt' - writing manifest file 'tutorial.egg-info/SOURCES.txt' - running build_ext - ...... - ---------------------------------------------------------------------- - Ran 6 tests in 0.181s - - OK - - - - |
