diff options
| author | Chris McDonough <chrism@plope.com> | 2012-03-17 13:39:36 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-03-17 13:39:36 -0400 |
| commit | 3c7d5c803c6e0cf3f51ffc0577b087e7c14855fb (patch) | |
| tree | 76b5db7cca027e9bcc1dc81be111b8bfae90a2b6 | |
| parent | 2c6f63a15ba4dff7aaddfa09a7066590c1a833ce (diff) | |
| parent | fca8f01565d99d841bed0dfb3c784042af693ab1 (diff) | |
| download | pyramid-3c7d5c803c6e0cf3f51ffc0577b087e7c14855fb.tar.gz pyramid-3c7d5c803c6e0cf3f51ffc0577b087e7c14855fb.tar.bz2 pyramid-3c7d5c803c6e0cf3f51ffc0577b087e7c14855fb.zip | |
Merge branch 'master' of github.com:Pylons/pyramid
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 76 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/basiclayout.rst | 15 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingmodels.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingviews.rst | 50 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/design.rst | 12 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/distributing.rst | 8 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/installation.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/tests.rst | 33 |
8 files changed, 105 insertions, 93 deletions
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index a40cbdb3a..8c1c50ff6 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -7,8 +7,8 @@ Adding Authorization :app:`Pyramid` provides facilities for :term:`authentication` and :term:`authorization`. We'll make use of both features to provide security to our application. Our application currently allows anyone with access to -the server to view, edit, and add pages to our wiki. We'll change our -application to allow only people whom possess a specific username (`editor`) +the server to view, edit, and add pages to our wiki. We'll change that +to allow only people who possess a specific username (`editor`) to add and edit wiki pages but we'll continue allowing anyone with access to the server to view pages. @@ -41,13 +41,12 @@ Open ``models.py`` and add the following statements: :language: python We're going to start to use a custom :term:`root factory` within our -``__init__.py`` file. The objects generated by the root factory will be used -as the :term:`context` of each request to our application. We do this to -allow :app:`Pyramid` declarative security to work properly. The context -object generated by the root factory during a request will be decorated with -security declarations. When we begin to use a custom root factory to generate -our contexts, we can begin to make use of the declarative security features -of :app:`Pyramid`. +``__init__.py`` file. The objects generated by the root factory will +be used as the :term:`context` of each request to our application. +Those context objects will be decorated with security +declarations. When we use a custom root factory to generate +our contexts, we can begin to make use of the declarative security +features of :app:`Pyramid`. We'll modify our ``__init__.py``, passing in a :term:`root factory` to our :term:`Configurator` constructor. We'll point it at the new class we created @@ -65,10 +64,12 @@ to a context is interpreted specially by :app:`Pyramid` as an access control list during view callable execution. See :ref:`assigning_acls` for more information about what an :term:`ACL` represents. -.. note: Although we don't use the functionality here, the ``factory`` used - to create route contexts may differ per-route as opposed to globally. See - the ``factory`` argument to - :meth:`pyramid.config.Configurator.add_route` for more info. +.. note:: + + Although we don't use the functionality here, the ``factory`` used + to create route contexts may differ per-route as opposed to globally. See + the ``factory`` argument to + :meth:`pyramid.config.Configurator.add_route` for more info. We'll pass the ``RootFactory`` we created in the step above in as the ``root_factory`` argument to a :term:`Configurator`. @@ -147,9 +148,10 @@ We've given the ``editor`` user membership to the ``group:editors`` by mapping him to this group in the ``GROUPS`` data structure (``GROUPS = {'editor':['group:editors']}``). Since the ``groupfinder`` function consults the ``GROUPS`` data structure, this will mean that, as a -result of the ACL attached to the root returned by the root factory, -and the permission associated with the ``add_page`` and ``edit_page`` -views, the ``editor`` user should be able to add and edit pages. +result of the ACL attached to the :term:`context` object returned by +the root factory, and the permission associated with the ``add_page`` +and ``edit_page`` views, the ``editor`` user should be able to add and +edit pages. Adding Login and Logout Views ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,20 +178,20 @@ The ``logout`` view callable will look something like this: :language: python The ``login`` view callable is decorated with two decorators, a -``@view_config`` decorators, which associates it with the ``login`` route, -the other a ``@forbidden_view_config`` decorator which turns it in to an -:term:`exception view` when Pyramid raises a -:class:`pyramid.httpexceptions.HTTPForbidden` exception. The one which -associates it with the ``login`` route makes it visible when we visit -``/login``. The other one makes it a :term:`forbidden view`. The forbidden -view is displayed whenever Pyramid or your application raises an -HTTPForbidden exception. In this case, we'll be relying on the forbidden -view to show the login form whenver someone attempts to execute an action -which they're not yet authorized to perform. +``@view_config`` decorator, which associates it with the ``login`` +route, and a ``@forbidden_view_config`` decorator which turns it in to +an :term:`exception view`. The one which associates it with the +``login`` route makes it visible when we visit ``/login``. The other +one makes it a :term:`forbidden view`. The forbidden view is +displayed whenever Pyramid or your application raises an +:class:`pyramid.httpexceptions.HTTPForbidden` exception. In this +case, we'll be relying on the forbidden view to show the login form +whenver someone attempts to execute an action which they're not yet +authorized to perform. The ``logout`` view callable is decorated with a ``@view_config`` decorator which associates it with the ``logout`` route. This makes it visible when we -visit ``/login``. +visit ``/logout``. We'll need to import some stuff to service the needs of these two functions: the ``pyramid.view.forbidden_view_config`` class, a number of values from the @@ -222,8 +224,8 @@ something like this to each view body: Return a logged_in flag to the renderer --------------------------------------- -We'll then change the return value of these views to pass the `resulting -`logged_in`` value to the template, e.g.: +We'll then change the return value of these views to pass the resulting +``logged_in`` value to the template, e.g.: .. code-block:: python :linenos: @@ -248,12 +250,14 @@ callables which these views reference cannot be invoked without the authenticated user possessing the ``edit`` permission with respect to the current :term:`context`. -Adding these ``permission`` arguments causes Pyramid to make the assertion -that only users who possess the effective ``edit`` permission at the time of -the request may invoke those two views. We've granted the ``group:editors`` -principal the ``edit`` permission at the root model via its ACL, so only the -a user whom is a member of the group named ``group:editors`` will able to -invoke the views associated with the ``add_page`` or ``edit_page`` routes. +Adding these ``permission`` arguments causes Pyramid to make the +assertion that only users who possess the effective ``edit`` +permission at the time of the request may invoke those two views. +We've granted the ``group:editors`` :term:`principal` the ``edit`` +permission in the :term:`root factory` via its ACL, so only a user who +is a member of the group named ``group:editors`` will be able to +invoke the views associated with the ``add_page`` or ``edit_page`` +routes. Adding the ``login.pt`` Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -322,7 +326,7 @@ each of the following URLs, check that the result is as expected: - ``http://localhost:6543/FrontPage`` invokes the ``view_page`` view of the FrontPage page object. -- ``http://localhost:6543/edit_page/FrontPage`` +- ``http://localhost:6543/FrontPage/edit_page`` invokes the edit view for the FrontPage object. It is executable by only the ``editor`` user. If a different user (or the anonymous user) invokes it, a login form will be displayed. Supplying the diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 39321a88e..6715b2bd7 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -74,7 +74,7 @@ dictionary of settings parsed from the ``.ini`` file, which contains deployment-related values such as ``pyramid.reload_templates``, ``db_string``, etc. -``'main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with +``main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with two arguments: ``static`` (the name), and ``static`` (the path): .. literalinclude:: src/basiclayout/tutorial/__init__.py @@ -123,10 +123,11 @@ Finally, ``main`` is finished configuring things, so it uses the View Declarations via ``views.py`` ---------------------------------- -Mapping a :term:`route` to code that will be executed when that route's -pattern matches is done by registering a :term:`view configuration`. Our -application uses the :meth:`pyramid.view.view_config` decorator to map view -callables to each route, thereby mapping URL patterns to code. +Mapping a :term:`route` to code that will be executed when a match for +the route's pattern occurs is done by registering a :term:`view +configuration`. Our application uses the +:meth:`pyramid.view.view_config` decorator to map view callables to +each route, thereby mapping URL patterns to code. Open ``tutorial/tutorial/views.py``. It should already contain the following: @@ -151,7 +152,7 @@ Note that ``my_view()`` accepts a single argument named ``request``. This is the standard call signature for a Pyramid :term:`view callable`. Remember in our ``__init__.py`` when we executed the -:meth:`pyramid.config.Configurator.scan` method, e.g. ``config.scan()``? The +:meth:`pyramid.config.Configurator.scan` method, i.e. ``config.scan()``? The purpose of calling the scan method was to find and process this ``@view_config`` decorator in order to create a view configuration within our application. Without being processed by ``scan``, the decorator effectively @@ -199,7 +200,7 @@ To give a simple example of a model class, we define one named ``MyModel``: :linenos: :language: py -Our sample model has an ``__init__`` that takes a two arguments (``name``, +Our example model has an ``__init__`` that takes a two arguments (``name``, and ``value``). It stores these values as ``self.name`` and ``self.value`` within the ``__init__`` function itself. The ``MyModel`` class also has a ``__tablename__`` attribute. This informs SQLAlchemy which table to use to diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index 27c602728..1653faf4a 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -57,7 +57,7 @@ attribute that will hold the body of each page. Changing ``scripts/initializedb.py`` ------------------------------------ -We haven't looked at the guts of this file yet, but within the ``scripts`` +We haven't looked at the details of this file yet, but within the ``scripts`` directory of your ``tutorial`` package is a file named ``initializedb.py``. Code in this file is executed whenever we run the ``initialize_tutorial_db`` command (as we did in the installation step of this tutorial). diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 92f61c486..ac58e1e46 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -157,17 +157,19 @@ that ``view_page()`` returns a dictionary (as opposed to a :term:`response` object) is a cue to :app:`Pyramid` 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 ``__init__.py``. +template, as indicated in the ``@view_config`` decorator that is applied to +``view_page()``. The ``add_page`` view function ------------------------------ -``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 -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. +``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 +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 +request passed to the ``add_page()`` view will have the values we need +to construct URLs and find model objects. .. literalinclude:: src/views/tutorial/views.py :lines: 45-56 @@ -179,17 +181,17 @@ the page we'd like to add. If our add view is invoked via, e.g. ``http://localhost:6543/add_page/SomeName``, the value for ``'pagename'`` in the ``matchdict`` will be ``'SomeName'``. -If the view execution is *not* a result of a form submission (if the +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 callable renders a template. To do so, it generates a "save url" which the template 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, 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 :app:`Pyramid` will render the template associated with this -view to a response. +we're going 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 object +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. -If the view execution *is* a result of a form submission (if the expression +If the view execution *is* a result of a form submission (i.e. the expression ``'form.submitted' in request.params`` is ``True``), we scrape 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 @@ -210,12 +212,12 @@ matching the name of the page the user wants to edit. :linenos: :language: python -If the view execution is *not* a result of a form submission (if the +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. -If the view execution *is* a result of a form submission (if the expression +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 @@ -231,11 +233,12 @@ The views we've added all reference a :term:`template`. Each template is a 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). +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: @@ -323,11 +326,14 @@ the order they're found in the ``__init__.py`` file. ``route_name='edit_page'``. As a result of our edits, the ``__init__.py`` file should look -something like so: +something like: .. literalinclude:: src/views/tutorial/__init__.py :linenos: :language: python + :emphasize-lines: 13-16 + +(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/design.rst b/docs/tutorials/wiki2/design.rst index 36c4f19bc..75122bd11 100644 --- a/docs/tutorials/wiki2/design.rst +++ b/docs/tutorials/wiki2/design.rst @@ -4,7 +4,7 @@ Design Following is a quick overview of our wiki application, to help us understand the changes that we will be doing next in our -default files generated by the paster scafffold. +default files generated by the ``alchemy`` scaffold. Overall ------- @@ -34,9 +34,10 @@ be used as the wiki home page. Views ----- -There will be four views to handle the normal operations of -viewing, editing and adding wiki pages. Two additional views -will handle the login and logout tasks related to security. +There will be four views to handle the normal operations of adding and +editing wiki pages, and viewing pages and the wiki front page. Two +additional views will handle the login and logout tasks related to +security. Security -------- @@ -59,7 +60,8 @@ Security | Allow | group:editors | Edit | +----------+----------------+----------------+ -- Permission declarations for the views. +- Permission declarations are added to the views to assert the + security policies as each request is handled. Summary diff --git a/docs/tutorials/wiki2/distributing.rst b/docs/tutorials/wiki2/distributing.rst index c80b43337..96293603c 100644 --- a/docs/tutorials/wiki2/distributing.rst +++ b/docs/tutorials/wiki2/distributing.rst @@ -27,12 +27,12 @@ The output of such a command will be something like: running sdist # ... more output ... creating dist - tar -cf dist/tutorial-0.1.tar tutorial-0.1 - gzip -f9 dist/tutorial-0.1.tar - removing 'tutorial-0.1' (and everything under it) + tar -cf dist/tutorial-0.0.tar tutorial-0.0 + gzip -f9 dist/tutorial-0.0.tar + removing 'tutorial-0.0' (and everything under it) Note that this command creates a tarball in the "dist" subdirectory -named ``tutorial-0.1.tar.gz``. You can send this file to your friends +named ``tutorial-0.0.tar.gz``. You can send this file to your friends to show them your cool new application. They should be able to install it by pointing the ``easy_install`` command directly at it. Or you can upload it to `PyPI <http://pypi.python.org>`_ and share it diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index a38205ace..4ee2728c2 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -141,7 +141,7 @@ On Windows: c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q -For a successful test run, you should see output like this:: +For a successful test run, you should see output that ends like this:: . ---------------------------------------------------------------------- diff --git a/docs/tutorials/wiki2/tests.rst b/docs/tutorials/wiki2/tests.rst index 86880fb18..f8dbb0f18 100644 --- a/docs/tutorials/wiki2/tests.rst +++ b/docs/tutorials/wiki2/tests.rst @@ -4,31 +4,30 @@ Adding Tests We will now add tests for the models and the views and a few functional tests in the ``tests.py``. Tests ensure that an application works, and -that it continues to work after some changes are made in the future. +that it continues to work after changes are made in the future. Testing the Models ================== -We write a test class for the model class ``Page`` and another test class -for the ``initialize_sql`` function. - -To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided as a -result of the ``alchemy`` scaffold. We'll add a test class named -``PageModelTests`` for the ``Page`` model. +To test the model class ``Page`` we'll add a new ``PageModelTests`` +class to our ``tests.py`` file that was generated as part of the +``alchemy`` scaffold. Testing the Views ================= -We'll modify our ``tests.py`` file, adding tests for each view function we -added above. As a result, we'll *delete* the ``ViewTests`` test in the file, -and add four other test classes: ``ViewWikiTests``, ``ViewPageTests``, -``AddPageTests``, and ``EditPageTests``. These test the ``view_wiki``, -``view_page``, ``add_page``, and ``edit_page`` views respectively. +We'll modify our ``tests.py`` file, adding tests for each view +function we added above. As a result, we'll *delete* the +``ViewTests`` class that the ``alchemy`` scaffold provided, and add +four other test classes: ``ViewWikiTests``, ``ViewPageTests``, +``AddPageTests``, and ``EditPageTests``. These test the +``view_wiki``, ``view_page``, ``add_page``, and ``edit_page`` views +respectively. Functional tests ================ -We test the whole application, covering security aspects that are not +We'll test the whole application, covering security aspects that are not tested in the unit tests, like logging in, logging out, checking that the ``viewer`` user cannot add or edit pages, but the ``editor`` user can, and so on. @@ -36,8 +35,7 @@ can, and so on. Viewing the results of all our edits to ``tests.py`` ==================================================== -Once we're done with the ``tests.py`` module, it will look a lot like the -below: +Once we're done with the ``tests.py`` module, it will look a lot like: .. literalinclude:: src/tests/tutorial/tests.py :linenos: @@ -55,6 +53,7 @@ Change the ``requires`` list in ``setup.py`` to include ``WebTest``. :linenos: :language: python :lines: 9-20 + :emphasize-lines: 10 After we've added a dependency on WebTest in ``setup.py``, we need to rerun ``setup.py develop`` to get WebTest installed into our virtualenv. Assuming @@ -88,12 +87,12 @@ On Windows: c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q -The expected result looks something like: +The expected result ends something like: .. code-block:: text ...................... ---------------------------------------------------------------------- - Ran 22 tests in 2.700s + Ran 21 tests in 2.700s OK |
