Permalink
Browse files

- The SQLAlchemy Wiki tutorial has been updated. It now uses

  ``@view_config`` decorators and an explicit database population script.

Closes #359.
  • Loading branch information...
1 parent 596495d commit 5edd54f05b05330fa6e899a1bb1650cc7a2df33c @mcdonc mcdonc committed Nov 27, 2011
Showing with 876 additions and 645 deletions.
  1. +5 −0 CHANGES.txt
  2. +1 −1 docs/_themes
  3. +5 −0 docs/glossary.rst
  4. +78 −75 docs/tutorials/wiki2/authorization.rst
  5. +20 −38 docs/tutorials/wiki2/basiclayout.rst
  6. +68 −36 docs/tutorials/wiki2/definingmodels.rst
  7. +81 −80 docs/tutorials/wiki2/definingviews.rst
  8. +1 −1 docs/tutorials/wiki2/installation.rst
  9. +0 −3 docs/tutorials/wiki2/src/authorization/README.txt
  10. +7 −1 docs/tutorials/wiki2/src/authorization/development.ini
  11. +1 −0 docs/tutorials/wiki2/src/authorization/production.ini
  12. +2 −1 docs/tutorials/wiki2/src/authorization/setup.py
  13. +6 −19 docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
  14. +0 −37 docs/tutorials/wiki2/src/authorization/tutorial/login.py
  15. +13 −24 docs/tutorials/wiki2/src/authorization/tutorial/models.py
  16. +1 −0 docs/tutorials/wiki2/src/authorization/tutorial/scripts/__init__.py
  17. +35 −0 docs/tutorials/wiki2/src/authorization/tutorial/scripts/populate.py
  18. +0 −1 docs/tutorials/wiki2/src/authorization/tutorial/security.py
  19. +16 −7 docs/tutorials/wiki2/src/authorization/tutorial/tests.py
  20. +64 −12 docs/tutorials/wiki2/src/authorization/tutorial/views.py
  21. +0 −3 docs/tutorials/wiki2/src/basiclayout/README.txt
  22. +7 −1 docs/tutorials/wiki2/src/basiclayout/development.ini
  23. +1 −0 docs/tutorials/wiki2/src/basiclayout/production.ini
  24. +2 −0 docs/tutorials/wiki2/src/basiclayout/setup.py
  25. +1 −0 docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
  26. +11 −26 docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
  27. +1 −0 docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/__init__.py
  28. +35 −0 docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/populate.py
  29. +1 −1 docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
  30. +17 −8 docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
  31. +5 −3 docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
  32. +0 −3 docs/tutorials/wiki2/src/models/README.txt
  33. +7 −1 docs/tutorials/wiki2/src/models/development.ini
  34. +1 −0 docs/tutorials/wiki2/src/models/production.ini
  35. +2 −0 docs/tutorials/wiki2/src/models/setup.py
  36. +6 −6 docs/tutorials/wiki2/src/models/tutorial/__init__.py
  37. +10 −23 docs/tutorials/wiki2/src/models/tutorial/models.py
  38. +1 −0 docs/tutorials/wiki2/src/models/tutorial/scripts/__init__.py
  39. +35 −0 docs/tutorials/wiki2/src/models/tutorial/scripts/populate.py
  40. +1 −1 docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
  41. +17 −7 docs/tutorials/wiki2/src/models/tutorial/tests.py
  42. +9 −5 docs/tutorials/wiki2/src/models/tutorial/views.py
  43. +0 −3 docs/tutorials/wiki2/src/tests/README.txt
  44. +7 −1 docs/tutorials/wiki2/src/tests/development.ini
  45. +1 −0 docs/tutorials/wiki2/src/tests/production.ini
  46. +2 −1 docs/tutorials/wiki2/src/tests/setup.py
  47. +6 −19 docs/tutorials/wiki2/src/tests/tutorial/__init__.py
  48. +0 −37 docs/tutorials/wiki2/src/tests/tutorial/login.py
  49. +13 −24 docs/tutorials/wiki2/src/tests/tutorial/models.py
  50. +1 −0 docs/tutorials/wiki2/src/tests/tutorial/scripts/__init__.py
  51. +36 −0 docs/tutorials/wiki2/src/tests/tutorial/scripts/populate.py
  52. +0 −1 docs/tutorials/wiki2/src/tests/tutorial/security.py
  53. +33 −30 docs/tutorials/wiki2/src/tests/tutorial/tests.py
  54. +64 −12 docs/tutorials/wiki2/src/tests/tutorial/views.py
  55. +0 −3 docs/tutorials/wiki2/src/views/README.txt
  56. +7 −1 docs/tutorials/wiki2/src/views/development.ini
  57. +1 −0 docs/tutorials/wiki2/src/views/production.ini
  58. +2 −0 docs/tutorials/wiki2/src/views/setup.py
  59. +5 −12 docs/tutorials/wiki2/src/views/tutorial/__init__.py
  60. +9 −21 docs/tutorials/wiki2/src/views/tutorial/models.py
  61. +1 −0 docs/tutorials/wiki2/src/views/tutorial/scripts/__init__.py
  62. +35 −0 docs/tutorials/wiki2/src/views/tutorial/scripts/populate.py
  63. +1 −1 docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
  64. +12 −5 docs/tutorials/wiki2/src/views/tutorial/tests.py
  65. +13 −4 docs/tutorials/wiki2/src/views/tutorial/views.py
  66. +2 −2 docs/tutorials/wiki2/tests.rst
  67. +0 −4 pyramid/scaffolds/__init__.py
  68. +10 −6 pyramid/scaffolds/alchemy/+package+/models.py
  69. +35 −0 pyramid/scaffolds/alchemy/+package+/scripts/populate.py
  70. +0 −30 pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl
  71. +1 −1 pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
  72. +5 −3 pyramid/scaffolds/alchemy/+package+/views.py_tmpl
View
@@ -86,6 +86,11 @@ Dependencies
- Pyramid no longer depends on the Paste or PasteScript packages.
+Documentation
+-------------
+
+- The SQLAlchemy Wiki tutorial has been updated. It now uses
+ ``@view_config`` decorators and an explicit database population script.
Scaffolds
---------
View
@@ -936,3 +936,8 @@ Glossary
email. See its `documentation
<https://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_.
+ console script
+ A script written to the ``bin`` (on UNIX, or ``Scripts`` on Windows)
+ directory of a Python installation or virtualenv as the result of
+ running ``setup.py install`` or ``setup.py develop``.
+
@@ -4,27 +4,22 @@
Adding Authorization
====================
-Our application currently allows anyone with access to the server to
-view, edit, and add pages to our wiki. For purposes of demonstration
-we'll change our application to allow only people whom 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.
-:app:`Pyramid` provides facilities for :term:`authorization` and
-:term:`authentication`. We'll make use of both features to provide security
-to our application.
-
-We will add an :term:`authentication policy` and an
-:term:`authorization policy` to our :term:`application
-registry`, add a ``security.py`` module, create a :term:`root factory`
-with an :term:`ACL`, and add :term:`permission` declarations to
-the ``edit_page`` and ``add_page`` views.
-
-Then we will add ``login`` and ``logout`` views, and modify the
-existing views to make them return a ``logged_in`` flag to the
-renderer.
-
-Finally, we will add a ``login.pt`` template and change the existing
-``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in.
+: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`)
+to add and edit wiki pages but we'll continue allowing anyone with access to
+the server to view pages.
+
+To do so, we'll add an :term:`authentication policy` and an
+:term:`authorization policy`. We'll also add a ``security.py`` module,
+create a :term:`root factory` with an :term:`ACL`, and add :term:`permission`
+declarations to the ``edit_page`` and ``add_page`` views. Then we'll add
+``login`` and ``logout`` views, and modify the existing views to make them
+return a ``logged_in`` flag to the renderer. Finally, we will add a
+``login.pt`` template and change the existing ``view.pt`` and ``edit.pt`` to
+show a "Logout" link when not logged in.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/authorization/
@@ -54,7 +49,7 @@ inside our ``models.py`` file. Add the following statements to your
``models.py`` file:
.. literalinclude:: src/authorization/tutorial/models.py
- :lines: 3-4,45-50
+ :lines: 1-4,35-39
:linenos:
:language: python
@@ -92,14 +87,14 @@ We'll change our ``__init__.py`` file to enable an
declarative security checking. We need to import the new policies:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 2-3,8
+ :lines: 2-3,7
:linenos:
:language: python
Then, we'll add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 15-21
+ :lines: 16-22
:linenos:
:language: python
@@ -111,49 +106,12 @@ represented by this policy: it is required. The ``callback`` is a
``groupfinder`` function in the current directory's ``security.py`` file. We
haven't added that module yet, but we're about to.
-We'll also change ``__init__.py``, adding a call to
-:meth:`pyramid.config.Configurator.add_view` that points at our ``login``
-:term:`view callable`. This is also known as a :term:`forbidden view`:
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 25,41-43
- :linenos:
- :language: python
-
-A forbidden view configures our newly created login view to show up when
-:app:`Pyramid` detects that a view invocation can not be authorized.
-
-A ``logout`` :term:`view callable` will allow users to log out later:
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 26,34
- :linenos:
- :language: python
-
-We'll also add ``permission`` arguments with the value ``edit`` to the
-``edit_page`` and ``add_page`` views. This indicates that the view
-callables which these views reference cannot be invoked without the
-authenticated user possessing the ``edit`` permission with respect to the
-current context.
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 37-40
- :linenos:
- :language: python
-
-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.
-
Viewing Your Changes
~~~~~~~~~~~~~~~~~~~~
-When we're done configuring a root factory, adding an authorization policy,
-and adding views, your application's ``__init__.py`` will look like this:
+When we're done configuring a root factory, adding a authentication and
+authorization policies, and adding routes for ``/login`` and ``/logout``,
+your application's ``__init__.py`` will look like this:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
@@ -191,30 +149,54 @@ views, the ``editor`` user should be able to add and edit pages.
Adding Login and Logout Views
-----------------------------
-We'll add a ``login`` view callable which renders a login form and
-processes the post from the login form, checking credentials.
+To our ``views.py`` we'll add a ``login`` view callable which renders a login
+form and processes the post from the login form, checking credentials.
We'll also add a ``logout`` view callable to our application and
provide a link to it. This view will clear the credentials of the
logged in user and redirect back to the front page.
-We'll add a different file (for presentation convenience) to add login
-and the logout view callables. Add a file named ``login.py`` to your
-application (in the same directory as ``views.py``) with the following
-content:
+The ``login`` view callable will look something like this:
-.. literalinclude:: src/authorization/tutorial/login.py
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: login
:linenos:
:language: python
+The ``logout`` view callable will look something like this:
+
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: logout
+ :linenos:
+ :language: python
+
+The ``login`` view callable is decorated with two ``@view_config``
+decorators, one which associates it with the ``login`` route, the other which
+associates it with the ``HTTPForbidden`` context. The one which associates
+it with the ``login`` route makes it visible when we visit ``/login``. The
+one which associates it with the ``HTTPForbidden`` context makes it the
+: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.
+
+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``.
+
+We'll need to import some stuff to service the needs of these two functions:
+the ``HTTPForbidden`` exception, a number of values from the
+``pyramid.security`` module, and a value from our newly added
+``tutorial.security`` package.
+
Changing Existing Views
-----------------------
Then we need to change each of our ``view_page``, ``edit_page`` and
-``add_page`` views in ``views.py`` to pass a "logged in" parameter to its
-template. We'll add something like this to each view body:
+``add_page`` view callables in ``views.py``. Within each of these views,
+we'll need to pass a "logged in" parameter to its template. We'll add
+something like this to each view body:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -224,7 +206,6 @@ template. We'll add something like this to each view body:
We'll then change the return value of these views to pass the `resulting
`logged_in`` value to the template, e.g.:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -233,6 +214,28 @@ We'll then change the return value of these views to pass the `resulting
logged_in = logged_in,
edit_url = edit_url)
+We'll also need to add a ``permission`` value to the ``@view_config``
+decorator for each of the ``add_page`` and ``edit_page`` view callables. For
+each, we'll add ``permission='edit'``, for example:
+
+.. code-block:: python
+ :linenos:
+
+ @view_config(route_name='edit_page', renderer='templates/edit.pt',
+ permission='edit')
+
+See the ``permission='edit'`` we added there? This indicates that the view
+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 the ``login.pt`` Template
--------------------------------
@@ -128,19 +128,20 @@ 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.
-Here is the code in the ``views.py`` file within our package:
+Here is the entirety of code in the ``views.py`` file within our package:
.. literalinclude:: src/basiclayout/tutorial/views.py
:linenos:
:language: py
-The important part to point out here is the ``@view_config`` decorator. In
-fact, ``@view_config`` is so important that we're going to ignore the rest of
-the code in the module at this point just to explain it. The
-``@view_config`` decorator associates the function it decorates with a
-:term:`view configuration`. The view configuration names a ``route_name``
-(``home``), and names a ``renderer``, which is a template which lives in the
-``templates`` subdirectory of the package.
+The important part to point out here is the ``@view_config`` decorator which
+sits atop the ``my_view`` function. In fact, ``@view_config`` is so
+important that we're going to ignore the rest of the code in the module at
+this point just to explain it. The ``@view_config`` decorator associates the
+function it decorates with a :term:`view configuration`. The view
+configuration names a ``route_name`` (``home``), and names a ``renderer``,
+which is a template which lives in the ``templates`` subdirectory of the
+package.
As the result of this view configuration, when the pattern associated with
the view named ``home`` is matched during a request, the function named
@@ -164,9 +165,14 @@ Content Models with ``models.py``
---------------------------------
In a SQLAlchemy-based application, a *model* object is an object composed by
-querying the SQL database. SQLAlchemy is an "object relational mapper" (an
-ORM). The ``models.py`` file is where the ``alchemy`` scaffold put the
-classes that implement our models.
+querying the SQL database. The ``models.py`` file is where the ``alchemy``
+scaffold put the classes that implement our models.
+
+Here is the complete source for ``models.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/models.py
+ :linenos:
+ :language: py
Let's take a look. First, we need some imports to support later code.
@@ -178,7 +184,7 @@ Let's take a look. First, we need some imports to support later code.
Next we set up a SQLAlchemy "DBSession" object:
.. literalinclude:: src/basiclayout/tutorial/models.py
- :lines: 15-16
+ :lines: 16
:linenos:
:language: py
@@ -202,30 +208,6 @@ within the ``__init__`` function itself. The ``MyModel`` class also has a
``__tablename__`` attribute. This informs SQLAlchemy which table to use to
store the data representing instances of this class.
-Next we define a function named ``populate`` which adds a single
-model instance into our SQL storage and commits a transaction:
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :pyobject: populate
- :linenos:
- :language: py
-
-The function doesn't do a lot in this case, but it's there to illustrate
-how an application requiring many objects to be set up could work.
-
-Lastly we have a function named ``initialize_sql`` which receives a SQL
-database engine and binds it to our SQLAlchemy DBSession object. It also
-calls the ``populate`` function, to do initial database population. This
-is the initialization function that is called from __init__.py above.
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :pyobject: initialize_sql
- :linenos:
- :language: py
-
-Here is the complete source for ``models.py``:
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :linenos:
- :language: py
+That's about all there is to it to models, views, and initialization code in
+our stock application.
Oops, something went wrong.

0 comments on commit 5edd54f

Please sign in to comment.