diff options
| author | Carlos de la Guardia <cguardia@hal9001.(none)> | 2011-03-15 20:35:02 -0400 |
|---|---|---|
| committer | Carlos de la Guardia <cguardia@hal9001.(none)> | 2011-03-15 20:35:02 -0400 |
| commit | 0aed1cdd37fb57663b978d1d8728d5b652b0b092 (patch) | |
| tree | 09057b3bf9ee1a397694e884ff225a599be7f6f1 /docs | |
| parent | 9fd15137314f304559479c1846d930a36b0e772e (diff) | |
| download | pyramid-0aed1cdd37fb57663b978d1d8728d5b652b0b092.tar.gz pyramid-0aed1cdd37fb57663b978d1d8728d5b652b0b092.tar.bz2 pyramid-0aed1cdd37fb57663b978d1d8728d5b652b0b092.zip | |
Restructured the routes wiki tutorial to make it easier to follow along. Moved the routes tutorial above the traversal tutorial.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/index.rst | 2 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/authorization.rst | 63 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/basiclayout.rst | 200 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingmodels.rst | 24 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/definingviews.rst | 28 | ||||
| -rw-r--r-- | docs/tutorials/wiki2/installation.rst | 61 |
6 files changed, 220 insertions, 158 deletions
diff --git a/docs/index.rst b/docs/index.rst index d55daccfe..d9a95f647 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -76,8 +76,8 @@ applications to various platforms. .. toctree:: :maxdepth: 2 - tutorials/wiki/index.rst tutorials/wiki2/index.rst + tutorials/wiki/index.rst tutorials/bfg/index.rst tutorials/gae/index.rst tutorials/modwsgi/index.rst diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst index fef74e4e2..0f3a9c31c 100644 --- a/docs/tutorials/wiki2/authorization.rst +++ b/docs/tutorials/wiki2/authorization.rst @@ -40,16 +40,10 @@ We'll modify our ``__init__.py``, passing in a :term:`root factory` to our inside our ``models.py`` file. Add the following statements to your ``models.py`` file: -.. code-block:: python - - from pyramid.security import Allow - from pyramid.security import Everyone - - class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] - def __init__(self, request): - pass +.. literalinclude:: src/authorization/tutorial/models.py + :lines: 3-4,45-49 + :linenos: + :language: python The ``RootFactory`` class we've just added will be used by :app:`Pyramid` to construct a ``context`` object. The context is @@ -84,16 +78,45 @@ For any :app:`Pyramid` application to perform authorization, we need to add a We'll change our ``__init__.py`` file to enable an ``AuthTktAuthenticationPolicy`` and an ``ACLAuthorizationPolicy`` to enable -declarative security checking. We'll also change ``__init__.py`` to add a -:meth:`pyramid.config.Configurator.add_view` call to points at our -``login`` :term:`view callable`, also known as a :term:`forbidden view`. +declarative security checking. + +.. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 15-21 + :linenos: + :language: python + +Note that that the +:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor +accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string +representing an encryption key used by the "authentication ticket" machinery +represented by this policy: it is required. The ``callback`` is a string, +representing a :term:`dotted Python name`, which points at the +``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`` to add a +:meth:`pyramid.config.Configurator.add_view` call that points at our +``login`` :term:`view callable`, also known as a :term:`forbidden view`: + +.. literalinclude:: src/authorization/tutorial/__init__.py + :lines: 24-26 + :linenos: + :language: python + This configures our newly created login view to show up when :app:`Pyramid` -detects that a view invocation can not be authorized. Also, we'll add +detects that a view invocation can not be authorized. + +Also, we'll add ``view_permission`` arguments with the value ``edit`` to the ``edit_page`` and ``add_page`` routes. This indicates that the view callables which these routes 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: 32-39 + :linenos: + :language: python + This makes 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 @@ -111,16 +134,6 @@ and adding views, your application's ``__init__.py`` will look like this: :linenos: :language: python -Note that that the -:class:`pyramid.authentication.AuthTktAuthenticationPolicy` constructor -accepts two arguments: ``secret`` and ``callback``. ``secret`` is a string -representing an encryption key used by the "authentication ticket" machinery -represented by this policy: it is required. The ``callback`` is a string, -representing a :term:`dotted Python name`, which points at the -``groupfinder`` function in the current directory's ``security.py`` file. We -haven't added that module yet, but we're about to. - - Adding ``security.py`` ~~~~~~~~~~~~~~~~~~~~~~ @@ -161,7 +174,7 @@ 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 logout view callables. Add a file named ``login.py`` to your +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: diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 17dcfc48f..c73009eb0 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -23,58 +23,93 @@ The generated ``development.ini`` file is read by ``paster`` which looks for the application module in the ``use`` variable of the ``app:tutorial`` section. The *entry point* is defined in the Setuptools configuration of this module, specifically in the ``setup.py`` file. For this tutorial, the *entry -point* is defined as ``tutorial:main`` and points to the following ``main`` -function: +point* is defined as ``tutorial:main`` and points to a function named ``main``. + +First we need some imports to support later code: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :end-before: main + :linenos: + :language: py + +Next we define the main function and create a SQLAlchemy database +engine from the ``sqlalchemy.`` prefixed settings in the ``development.ini` +`file's ``[app:tutorial]`` section. This will be a URI (something like +``sqlite://``): .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 6-9 :linenos: :language: py -#. *Lines 1-4*. Imports to support later code. - -#. *Line 9*. Create a SQLAlchemy database engine from the ``sqlalchemy.`` - prefixed settings in the ``development.ini`` file's ``[app:tutorial]`` - section. This will be a URI (something like ``sqlite://``). - -#. *Line 10*. We initialize our SQL database using SQLAlchemy, passing - it the engine - -#. *Line 11*. We construct a :term:`Configurator`. ``settings`` is - passed as a keyword argument with the dictionary values passed by - PasteDeploy as the ``settings`` argument. This will be a - dictionary of settings parsed by PasteDeploy, which contains - deployment-related values such as ``reload_templates``, - ``db_string``, etc. - -#. *Line 12*. We call - :meth:`pyramid.config.Configurator.add_static_view` with the - arguments ``static`` (the name), and ``tutorial:static`` (the path). This - registers a static resource view which will match any URL that starts with - ``/static/``. This will serve up static resources for us from within the - ``static`` directory of our ``tutorial`` package, in this case, - via ``http://localhost:6543/static/`` and below. With this declaration, - we're saying that any URL that starts with ``/static`` should go to the - static view; any remainder of its path (e.g. the ``/foo`` in - ``/static/foo``) will be used to compose a path to a static file resource, - such as a CSS file. - -#. *Lines 13-14*. Register a :term:`route configuration` via the - :meth:`pyramid.config.Configurator.add_route` method that will be - used when the URL is ``/``. Since this route has a ``pattern`` equalling - ``/`` it is the "default" route. The argument named ``view`` with the - value ``tutorial.views.my_view`` is the dotted name to a *function* we - write (generated by the ``pyramid_routesalchemy`` template) that is given - a ``request`` object and which returns a response or a dictionary. You - will use :meth:`pyramid.config.Configurator.add_route` statements - in a :term:`URL dispatch` based application to map URLs to code. This - route also names a ``view_renderer``, which is a template which lives in - the ``templates`` subdirectory of the package. When the - ``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` - will use this template to create a response. - -#. *Line 15*. We use the - :meth:`pyramid.config.Configurator.make_wsgi_app` method to return - a :term:`WSGI` application. +We then initialize our SQL database using SQLAlchemy, passing +it the engine: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 10 + :language: py + +The next step is to construct a :term:`Configurator`: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 11 + :language: py + +``settings`` is passed as a keyword argument with the dictionary values +passed by PasteDeploy as the ``settings`` argument. This will be a +dictionary of settings parsed by PasteDeploy, which contains +deployment-related values such as ``reload_templates``, +``db_string``, etc. + +We now can call :meth:`pyramid.config.Configurator.add_static_view` with the +arguments ``static`` (the name), and ``tutorial:static`` (the path): + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 12 + :language: py + +This registers a static resource view which will match any URL that starts with +``/static/``. This will serve up static resources for us from within the +``static`` directory of our ``tutorial`` package, in this case, +via ``http://localhost:6543/static/`` and below. With this declaration, +we're saying that any URL that starts with ``/static`` should go to the +static view; any remainder of its path (e.g. the ``/foo`` in +``/static/foo``) will be used to compose a path to a static file resource, +such as a CSS file. + +Using the configurator we can also register a :term:`route configuration` +via the :meth:`pyramid.config.Configurator.add_route` method that will be +used when the URL is ``/``: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 13-14 + :language: py + +Since this route has a ``pattern`` equalling +``/`` it is the "default" route. The argument named ``view`` with the +value ``tutorial.views.my_view`` is the dotted name to a *function* we +write (generated by the ``pyramid_routesalchemy`` template) that is given +a ``request`` object and which returns a response or a dictionary. + +You will use :meth:`pyramid.config.Configurator.add_route` statements +in a :term:`URL dispatch` based application to map URLs to code. This +route also names a ``view_renderer``, which is a template which lives in +the ``templates`` subdirectory of the package. When the +``tutorial.views.my_view`` view returns a dictionary, a :term:`renderer` +will use this template to create a response. + +Fimnally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app` +method to return a :term:`WSGI` application: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 15 + :language: py + +Our final __init__.py file will look like this: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :linenos: + :language: py Content Models with ``models.py`` --------------------------------- @@ -85,34 +120,65 @@ SQLAlchemy is an "object relational mapper" (an ORM). The ``models.py`` file is where the ``pyramid_routesalchemy`` Paster template put the classes that implement our models. -Here is the source for ``models.py``: +Let's take a look. First, we need some imports to support later code. .. literalinclude:: src/basiclayout/tutorial/models.py + :end-before: DBSession :linenos: :language: py -#. *Lines 1-13*. Imports to support later code. +Next we set up a SQLAlchemy "DBSession" object: + + .. literalinclude:: src/basiclayout/tutorial/models.py + :lines: 15-16 + :linenos: + :language: py -#. *Line 15*. We set up a SQLAlchemy "DBSession" object here. We - specify that we'd like to use the "ZopeTransactionExtension". This - extension is an extension which allows us to use a *transaction - manager* instead of controlling commits and aborts to database - operations by hand. +We also need to create a declarative ``Base`` object to use as a +base class for our model: -#. *Line 16*. We create a declarative ``Base`` object to use as a - base class for our model. + .. literalinclude:: src/basiclayout/tutorial/models.py + :lines: 17 + :language: py + +To give a simple example of a model class, we define one named ``MyModel``: + + .. literalinclude:: src/basiclayout/tutorial/models.py + :pyobject: MyModel + :linenos: + :language: py -#. *Lines 18-26*. A model class named ``MyModel``. It 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 store the data representing instances of this class. +Our sample 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 store the data representing instances of this class. -#. *Lines 28-33*. A function named ``populate`` which adds a single - model instance into our SQL storage and commits a transaction. +Next we define a function named ``populate`` which adds a single +model instance into our SQL storage and commits a transaction: -#. *Lines 35-42*. 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. + .. 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 diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index 09e1f26c3..117442a1c 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -31,8 +31,14 @@ application, this class should inherit from an instance of :class:`sqlalchemy.ext.declarative.declarative_base`. Declarative SQLAlchemy models are easier to use than directly-mapped ones. -Our ``Page`` class will have a class level attribute ``__tablename__`` which -equals the string ``pages``. This means that SQLAlchemy will store our wiki +.. literalinclude:: src/models/tutorial/models.py + :pyobject: Page + :linenos: + :language: python + +As you can see, our ``Page`` class has a class level attribute +``__tablename__`` which equals the string ``pages``. +This means that SQLAlchemy will store our wiki data in a SQL table named ``pages``. Our Page class will also have class-level attributes named ``id``, ``pagename`` and ``data`` (all instances of :class:`sqlalchemy.Column`). These will map to columns in the ``pages`` @@ -44,15 +50,21 @@ will hold the body of each page. We'll also remove our ``populate`` function. We'll inline the populate step into ``initialize_sql``, changing our ``initialize_sql`` function to add a FrontPage object to our database at startup time. -We're also going to use slightly different binding syntax. It will -will otherwise largely be the same as the ``initialize_sql`` in the + +.. literalinclude:: src/models/tutorial/models.py + :pyobject: initialize_sql + :linenos: + :language: python + +Here, we're using a slightly different binding syntax. It is +otherwise largely the same as the ``initialize_sql`` in the paster-generated ``models.py``. Our DBSession assignment stays the same as the original generated ``models.py``. -Looking at the Result of Our Edits to ``models.py`` ---------------------------------------------------- +Looking at the Result of all Our Edits to ``models.py`` +------------------------------------------------------- The result of all of our edits to ``models.py`` will end up looking something like this: diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst index 73f728132..3fa9bbccd 100644 --- a/docs/tutorials/wiki2/definingviews.rst +++ b/docs/tutorials/wiki2/definingviews.rst @@ -83,7 +83,14 @@ 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 +represents the path to our "FrontPage". + +.. literalinclude:: src/views/tutorial/views.py + :pyobject: view_wiki + :linenos: + :language: python + +It returns an instance of the :class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement the WebOb :term:`response` interface), It will use the :func:`pyramid.url.route_url` API to construct a URL to the @@ -101,6 +108,11 @@ 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. +.. literalinclude:: src/views/tutorial/views.py + :pyobject: view_page + :linenos: + :language: python + 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 @@ -134,6 +146,11 @@ 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 + :pyobject: add_page + :linenos: + :language: python + 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`` @@ -168,6 +185,11 @@ it also acts as the handler for the form it renders. The will have a ``pagename`` key matching the name of the page the user wants to edit. +.. literalinclude:: src/views/tutorial/views.py + :pyobject: edit_page + :linenos: + :language: python + 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 @@ -180,8 +202,8 @@ If the view execution *is* a result of a form submission (if the expression attribute of the page object. 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`` -=============================================== +Viewing the Result of all Our Edits to ``views.py`` +=================================================== The result of all of our edits to ``views.py`` will leave it looking like this: diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index bee348de6..ed81e3774 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -2,10 +2,9 @@ Installation ============ -For the most part, the installation process for this tutorial -duplicates the steps described in :ref:`installing_chapter` and -:ref:`project_narr`, however it also explains how to install -additional libraries for tutorial purposes. +This tutorial assumes that Python and virtualenv are already installed +and working in your system. If you need help setting this up, you should +refer to the chapters on :ref:`installing_chapter`. Preparation =========== @@ -22,32 +21,7 @@ Preparation, UNIX manager. For example, on a Debian Linux system, do ``sudo apt-get install libsqlite3-dev``. -#. If you don't already have a Python 2.6 interpreter installed on - your system, obtain, install, or find `Python 2.6 - <http://www.python.org/download/releases/2.6.6/>`_ for your system. - -#. Make sure the Python development headers are installed on your system. If - you've installed Python from source, these will already be installed. If - you're using a system Python, you may have to install a ``python-dev`` - package (e.g. ``apt-get python-dev``). The headers are not required for - Pyramid itself, just for dependencies of the tutorial. - -#. Install the latest `setuptools` into the Python you - obtained/installed/found in the step above: download `ez_setup.py - <http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using - the ``python`` interpreter of your Python 2.6 installation: - - .. code-block:: text - - $ /path/to/my/Python-2.6/bin/python ez_setup.py - -#. Use that Python's `bin/easy_install` to install `virtualenv`: - - .. code-block:: text - - $ /path/to/my/Python-2.6/bin/easy_install virtualenv - -#. Use that Python's virtualenv to make a workspace: +#. Use your Python's virtualenv to make a workspace: .. code-block:: text @@ -59,9 +33,6 @@ Preparation, UNIX $ cd pyramidtut -#. (Optional) Consider using ``source bin/activate`` to make your - shell environment wired to use the virtualenv. - #. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies installed: @@ -79,26 +50,7 @@ Preparation, UNIX Preparation, Windows -------------------- -#. Install, or find `Python 2.6.6 - <http://python.org/download/releases/2.6.6/>`_ for your system. - -#. Install the latest `setuptools` into the Python you - obtained/installed/found in the step above: download `ez_setup.py - <http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using - the ``python`` interpreter of your Python 2.6 installation using a - command prompt: - - .. code-block:: text - - c:\> c:\Python26\python ez_setup.py - -#. Use that Python's `bin/easy_install` to install `virtualenv`: - - .. code-block:: text - - c:\> c:\Python26\Scripts\easy_install virtualenv - -#. Use that Python's virtualenv to make a workspace: +#. Use your Python's virtualenv to make a workspace: .. code-block:: text @@ -110,9 +62,6 @@ Preparation, Windows c:\> cd pyramidtut -#. (Optional) Consider using ``bin\activate.bat`` to make your shell - environment wired to use the virtualenv. - #. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies installed: |
