Commits

Ches Martin committed 9b9fcf9

remove mention of google_app_engine option, etc.

  • Participants
  • Parent commits 02aa455

Comments (0)

Files changed (1)

File quickwiki-097.patch

  ==================
  
  Introduction
-@@ -9,59 +9,69 @@
+@@ -9,59 +9,73 @@
  
  If you haven't done so already read the :ref:`getting_started` guide.
  
 +.. note::
  
 -    The default ``sqlite:///%(here)s/quickwiki.db`` uses a (file-based) SQLite database named ``quickwiki.db`` in the project's top-level directory. This database will be created for you when running the ``paster setup-app`` command below, but you could also use MySQL, Oracle or PostgreSQL. Firebird and MS-SQL may also work. See the `SQLAlchemy documentation <http://www.sqlalchemy.org/docs/04/dbengine.html#dbengine_establishing>`_ for more information on how to connect to different databases. SQLite for example requires additional forward slashes in its URI, where the client/server databases should only use two. You will also need to make sure you have the appropriate Python driver for the database you wish to use. If you're using Python 2.5, a version of the `pysqlite adapter <http://www.initd.org/tracker/pysqlite/wiki/pysqlite>`_ is already included, so you can jump right in with the tutorial. You may need to get `SQLite itself <http://www.sqlite.org/download.html>`_.
-+    The default ``sqlite:///production.db`` uses a (file-based) SQLite database named :file:`production.db` in the project's top-level directory. This database will be created for you when running the :command:`paster setup-app` command below, but you could also use MySQL, Oracle or PostgreSQL. Firebird and MS-SQL may also work. See the `SQLAlchemy documentation <http://www.sqlalchemy.org/docs/04/dbengine.html#dbengine_establishing>`_ for more information on how to connect to different databases. SQLite for example requires additional forward slashes in its URI, where the client/server databases should only use two. You will also need to make sure you have the appropriate Python driver for the database you wish to use. If you're using Python 2.5, a version of the `pysqlite adapter <http://www.initd.org/tracker/pysqlite/wiki/pysqlite>`_ is already included, so you can jump right in with the tutorial. You may need to get `SQLite itself <http://www.sqlite.org/download.html>`_.
++    The default ``sqlite:///production.db`` uses a (file-based) SQLite database named :file:`production.db` in the project's top-level directory. This database will be created for you when running the :command:`paster setup-app` command below, but you could also use other RDBM systems like MySQL, Oracle or PostgreSQL. See the SQLAlchemy documentation for more `information on how to connect to different databases`_. You will also need to make sure you have the appropriate Python driver for the database you wish to use. If you're using Python 2.5, a version of the `pysqlite adapter`_ is already included, so you can jump right in with the tutorial. You may need to install `SQLite itself`_.
  
- Finally create the database tables and serve the finished application:
+-Finally create the database tables and serve the finished application:
++Finally, create the database tables and serve the finished application:
  
  .. code-block:: bash
  
 +    $ paster serve quickwiki.ini
  
 -That's it! Now you can visit http://127.0.0.1:5000 and experiment with the finished Wiki. Note that in the title list screen you can drag page titles to the trash area to delete them via AJAX calls.
-+That's it! Now you can visit http://localhost:5000 and experiment with the finished Wiki.
++That's it! Now you can visit http://localhost:5000 and experiment with the finished wiki.
  
 -When you've finished, stop the server with ``CTRL+C`` because we will start developing our own version.
 +When you've finished, stop the server with :kbd:`Ctrl+C` because we will start developing our own version.
  
 -If you are interested in looking at the latest version of the QuickWiki source code it can be browsed online at http://www.knowledgetap.com/hg/QuickWiki or can be checked out using Mercurial:
-+If you are interested in looking at the latest version of the QuickWiki source code, it can be browsed online at http://www.knowledgetap.com/hg/QuickWiki, or can be checked out using Mercurial:
++If you would like to refer to the latest version of the QuickWiki source code, it can be browsed online at http://www.knowledgetap.com/hg/QuickWiki, or can be checked out using `Mercurial`_:
  
  .. code-block:: bash
  
 +.. note::
  
 -    To run the version checked out from the repository, you'll want to run ``python setup.py egg_info`` from the project's root directory. This will generate some files in the ``QuickWiki.egg-info`` directory.
--
++    To run the version checked out from the repository, you'll want to run :command:`python setup.py egg_info` from the project's root directory. This will generate some files in the :file:`QuickWiki.egg-info` directory -- because ``egg-info`` contains generated, version- and build-specific distribution metadata, you should normally exclude it from version control in your own projects.
+ 
 -Note that there is also currently a small bug where running the command doesn't generate a ``paster_plugins.txt`` file in the ``egg-info`` directory. Without this, ``paster shell`` will not work. Create it yourself, and add the text ``Pylons``, ``WebHelpers`` and ``PasteScript`` on separate lines. Hopefully this issue will be fixed soon.
-+    To run the version checked out from the repository, you'll want to run :command:`python setup.py egg_info` from the project's root directory. This will generate some files in the :file:`QuickWiki.egg-info` directory.
++.. _information on how to connect to different databases: http://www.sqlalchemy.org/docs/05/dbengine.html#create-engine-url-arguments
++.. _pysqlite adapter: http://www.initd.org/tracker/pysqlite/wiki/pysqlite
++.. _SQLite itself: http://www.sqlite.org/download.html
++.. _Mercurial: http://www.selenic.com/mercurial/wiki/
  
  Developing QuickWiki
  ====================
  
 -If you skipped the "Starting at the End" section you will need to assure that you have Pylons installed. See the :ref:`getting_started`.
-+If you skipped the "Starting at the End" section you will need to assure that you have Pylons installed. See :ref:`getting_started`.
++If you skipped the `Starting at the End`_ section you will need to assure that you have Pylons installed. See :ref:`getting_started`.
  
 -Then create your project:
 +Begin creating your project with the following command:
 +
 +Pylons features an interactive project setup script that can pre-configure your application with some common options based on your selections. You will be prompted to choose:
 +
-+1. which template engine you wish to use. Pylons can set up your project for Mako, Genshi or Jinja 2 out of the box. For QuickWiki, press ``Return`` to accept the default, ``mako``.
-+2. whether or not to configure the project for :term:`SQLAlchemy`. This will add basic database connectivity so you can start working with model classes immediately after specifying the location of your database in a config file. Enter ``True`` and press ``Return`` to continue.
-+3. whether or not to prepare the project to run on :term:`Google App Engine`. Accept the default, ``False``.
++1. which template engine you wish to use. Pylons can set up your project for Mako, Genshi or Jinja 2 out of the box. For QuickWiki, press :kbd:`Return` to accept the default, ``mako``.
++2. whether or not to configure the project for :term:`SQLAlchemy`. This will add basic database connectivity so you can begin working with model objects immediately after specifying the location of your database in a config file. Enter ``True`` and press :kbd:`Return` to continue.
 +
 +Alternatively, configuration options can be specified non-interactively with command line options such as:
 +
 +.. code-block:: bash
 +
-+    $ paster create -t pylons QuickWiki sqlalchemy=true template_engine=mako google_app_engine=false
++    $ paster create -t pylons QuickWiki sqlalchemy=true template_engine=mako
  
  Now lets start the server and see what we have:
  
-@@ -70,46 +80,45 @@
+@@ -70,46 +84,44 @@
      $ cd QuickWiki
      $ paster serve --reload development.ini
  
 +.. note::
  
 -Open a new console and ``cd QuickWiki/quickwiki``. Visit http://127.0.0.1:5000 where you will see the introduction page. Delete the file ``public/index.html`` because we want to see the front page of the wiki instead of this welcome page. If you now refresh the page, the Pylons built-in error document support will kick in and display an ``Error 404`` page to tell you the file could not be found. We'll setup a controller to handle this location later.
-+    We have started the server with the ``--reload`` switch. This means any changes we make to code will cause the server to restart (if necessary); your changes are immediately reflected on the live site.
++    We have started the server with the :option:`--reload` switch. This means any changes we make to code will cause the server to restart (if necessary); your changes are immediately reflected on the live site.
 +
-+Open a new console and navigate to the :file:`QuickWiki/quickwiki` subdirectory of the project. Visit http://localhost:5000 where you will see the introduction page. Delete the file :file:`public/index.html` because we want to see the front page of the wiki instead of this welcome page. If you now refresh the page, the Pylons built-in error document support will kick in and display an ``Error 404`` page to tell you the file could not be found. We'll setup a controller to handle this location later.
++Open a new console and navigate to the :file:`QuickWiki/quickwiki` subdirectory of the project. Visit http://localhost:5000 where you will see the introduction page. Delete the file :file:`public/index.html` because we want to see the front page of the wiki instead of this welcome page. If you now refresh the page, the Pylons built-in error document support will kick in and display an ``Error 404`` page to tell you the file could not be found. We'll setup a controller to handle this location shortly.
  
  The Model
  =========
  
 -Pylons uses a Model View Controller architecture; we'll start by creating the model. We could use any system we like for the model including `SQLObject <http://www.sqlobject.org>`_ or `SQLAlchemy <http://www.sqlalchemy.org>`_. SQLAlchemy is the default for current versions of Pylons, and we'll use it for QuickWiki.
-+Pylons uses a :term:`Model-View-Controller` architecture; we'll start by creating the model. You could use any persistence layer or :term:`ORM` you like for the model in your applications, like :term:`SQLAlchemy`, `Storm <http://storm.canonical.com/>`_, or even `CouchDb <http://couchdb.apache.org/>`_ or the :term:`Google App Engine` `Datastore <http://code.google.com/appengine/docs/datastore/>`_. :term:`SQLAlchemy` is commonly used by many Python developers and Pylons includes the special project template support that we've selected for QuickWiki.
++Pylons uses a :term:`Model-View-Controller` architecture; we'll start by creating the model, the center of our application's domain logic. You could use any persistence layer or :term:`ORM` you like for the model in your applications, like :term:`SQLAlchemy`, `Storm`_, or even `CouchDb`_ or the :term:`Google App Engine` `Datastore`_. :term:`SQLAlchemy` is commonly used by many Python developers and Pylons includes the special project template support that we've selected for QuickWiki.
  
 -.. Note:: SQLAlchemy is a Python SQL toolkit and Object Relational Mapper that is quite popular among many Python programmers.
-+:term:`SQLAlchemy` provides a full suite of well-known enterprise-level persistence patterns, designed for efficient and high-performance database access, adapted into a simple and Pythonic domain language. There is full and detailed documentation available on the :term:`SQLAlchemy` website at http://www.sqlalchemy.org/docs/ and you will want to become familiar with this before you get heavily into :term:`SQLAlchemy`.
++:term:`SQLAlchemy` provides a full suite of well-known enterprise-level persistence patterns, designed for efficient and high-performance database access, adapted into a simple and Pythonic domain language. There is `full and detailed documentation`_ available on the official :term:`SQLAlchemy` website and you will want to become familiar with this before working at length with it.
  
 -SQLAlchemy provides a full suite of well known enterprise-level persistence patterns, designed for efficient and high-performance database access, adapted into a simple and Pythonic domain language. There is full and detailed documentation available on the SQLAlchemy website at http://sqlalchemy.org/docs/ and you should really read this before you get heavily into SQLAlchemy.
-+TODO: Doc guidelines; should we only reference glossary on first mention of a term? In the doc, section, paragraph?
 +TODO: Once again, info on the SQLA session needs to be cleaned and clarified.
-+The most basic way of using SQLAlchemy is with explicit sessions where you create :class:`Session` objects as needed. Pylons applications typically employ a slightly more sophisticated setup using SQLAlchemy 0.4's "contextual," thread-local sessions, via ``scoped_session``. With this configuration, the application can use a single :class:`Session` instance per web request, without the need to pass it around explicitly. Instantiating a new :class:`Session` will actually find an existing one in the current thread if available. This is also covered in the document `Using SQLAlchemy with Pylons <http://wiki.pylonshq.com/display/pylonsdocs/Using+SQLAlchemy+with+Pylons>`_, and you can learn further details in the `SQLAlchemy documentation on the Session <http://www.sqlalchemy.org/docs/04/session.html#unitofwork_contextual>`_.
++The most basic way of using :term:`SQLAlchemy` is with explicit sessions where you create :class:`Session` objects as needed. Pylons applications typically employ a slightly more sophisticated setup using :term:`SQLAlchemy` 0.4's "contextual," thread-local sessions, via ``scoped_session``. With this configuration, the application can use a single :class:`Session` instance per web request, without the need to pass it around explicitly. Instantiating a new :class:`Session` will actually find an existing one in the current thread if available. This is also covered in the document `Using SQLAlchemy with Pylons <http://wiki.pylonshq.com/display/pylonsdocs/Using+SQLAlchemy+with+Pylons>`_, and you can learn further details in the `SQLAlchemy documentation on the Session <http://www.sqlalchemy.org/docs/04/session.html#unitofwork_contextual>`_.
  
 -The most basic way of using SQLAlchemy is with explicit sessions where you create ``Session`` objects as needed. Pylons applications typically employ a slightly more sophisticated setup using SQLAlchemy 0.4's "contextual," thread-local sessions, via ``scoped_session``. With this configuration, the application can use a single ``Session`` instance per web request, without the need to pass it around explicitly. Instantiating a new ``Session`` will actually find an existing one in the current thread if available. There are further details in the `SQLAlchemy documentation on the Session <http://www.sqlalchemy.org/docs/04/session.html#unitofwork_contextual>`_.
 +.. note::
  
 -.. Note::
 -    It is important to recognize the difference between SQLAlchemy's (or possibly another DB abstraction layer's) ``Session`` object and Pylons' standard ``session`` (with a lowercase 's') for web requests. See :mod:`beaker` for more on the latter. It is customary to reference the database session by ``model.Session`` outside of model classes.
-+    It is important to recognize the difference between SQLAlchemy's (or possibly another DB abstraction layer's) :class:`Session` object and Pylons' standard ``session`` (with a lowercase 's') for web requests. See :mod:`beaker` for more on the latter. It is customary to reference the database session by ``model.Session`` outside of model classes.
++    It is important to recognize the difference between :term:`SQLAlchemy`'s (or possibly another DB abstraction layer's) :class:`Session` object and Pylons' standard ``session`` (with a lowercase 's') for web requests. See :mod:`beaker` for more on the latter. It is customary to reference the database session by ``model.Session`` outside of model classes.
  
 -
 -Now add the following to the end of the contents of your ``model/__init__.py`` file:
-+Now open your :file:`model/__init__.py` file, delete the commented examples below the :func:`init_model` function, and add the following in its place (taking care that it is not indented):
++Now open your :file:`model/__init__.py` file, delete the commented examples below the body of the :func:`init_model` function, and add the following in its place (taking care that it is not indented):
  
  .. code-block:: python
  
 +TODO: update this...
  The first line imports Pylons' ``config`` object so we can bind our database ``Session`` to an engine -- more on that in a bit. The second line imports some useful SQLAlchemy objects such as the ``Table`` and ``Column`` classes. The third imports the mapper function which we use to map our table schemas to objects. The final import statement provides two functions for setting up the session and adding the contextual functionality.
  
- After the imports we setup our ``metadata`` object which is used when defining and managing tables. We then define a table called ``pages`` which has two columns, ``title`` (the primary key) and ``content``.
+-After the imports we setup our ``metadata`` object which is used when defining and managing tables. We then define a table called ``pages`` which has two columns, ``title`` (the primary key) and ``content``.
++After the imports we setup our :obj:`metadata` object which is used when defining and managing tables. We then define a table called ``pages`` which has two columns, ``title`` (the primary key) and ``content``.
  
 -.. Note::
 -    SQLAlchemy also supports reflecting table information directly from a database. If we had already created the ``pages`` database table, SQLAlchemy could have constructed the ``pages_table`` object for us. This uses the ``autoload=True`` parameter in place of the ``Column`` definitions, like this:
 +.. note::
 +
-+TODO: check docs on the :data: signifier; see if this autoload stuff is still correct
++TODO: see if this autoload stuff is still correctly shown
 +    SQLAlchemy also supports reflecting table information directly from a database. If we had already created the ``pages`` database table, SQLAlchemy could have constructed the :data:`pages_table` object for us. This uses the ``autoload=True`` parameter in place of the :class:`Column` definitions, like this:
  
  .. code-block:: python
  
-@@ -117,11 +126,13 @@
+@@ -117,11 +129,13 @@
  
  `SQLAlchemy table reflection docs <http://www.sqlalchemy.org/docs/04/metadata.html#metadata_tables_reflecting>`_
  
  
  .. code-block:: python
  
-@@ -129,13 +140,14 @@
+@@ -129,13 +143,14 @@
          def __str__(self):
              return self.title
  
  
  .. code-block:: python
  
-@@ -147,8 +159,16 @@
+@@ -147,8 +162,16 @@
      import quickwiki.lib.helpers as h
  
      wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)", re.UNICODE)
  
  .. code-block:: python
  
-@@ -159,28 +179,30 @@
+@@ -159,36 +182,38 @@
              return self.title
  
          def get_wiki_content(self):
 +    Pylons uses a :term:`Model-View-Controller` architecture and so the formatting of objects into HTML should usually be handled in the view, i.e. in a template. In this example converting reStructuredText into HTML in a template is not appropriate so we are treating the HTML representation of the content as part of the model. It also gives us the chance to demonstrate that SQLAlchemy domain classes are real Python classes that can have their own methods.
  
 -One final change, since we have used docutils and SQLAlchemy, both third party packages, we need to edit our ``setup.py`` file so that anyone installing QuickWiki with `Easy Install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ will automatically also have these dependencies installed for them too. Edit your ``setup.py`` in your project root directory so that the ``install_requires`` line looks like this:
-+One final change, since we have used docutils and SQLAlchemy, both third party packages, we need to edit our :file:`setup.py` file so that anyone installing QuickWiki with `Easy Install <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ will automatically also have these dependencies installed for them too. Edit your :file:`setup.py` in your project root directory so that the ``install_requires`` line looks like this:
++One final change, since we have used docutils and SQLAlchemy, both third party packages, we need to edit our :file:`setup.py` file so that anyone installing QuickWiki with `easy_install`_ will automatically also have these dependencies installed for them too. Edit your :file:`setup.py` in your project root directory so that the ``install_requires`` line looks like this:
  
  .. code-block:: python
  
-     install_requires=["Pylons>=0.9.6", "docutils==0.4", "SQLAlchemy>=0.4.1"],
+-    install_requires=["Pylons>=0.9.6", "docutils==0.4", "SQLAlchemy>=0.4.1"],
++    install_requires=["Pylons>=0.9.7", "docutils==0.4", "SQLAlchemy>=0.5.0rc4"],
  
 -While we are we are making changes to ``setup.py`` we might want to complete some of the other sections too. Set the version number to 0.1.5 and add a description and URL which will be used on the Python Cheeseshop when we release it:
 +While we are we are making changes to :file:`setup.py` we might want to complete some of the other sections too. Set the version number to 0.1.6 and add a description and URL which will be used on the Python Cheeseshop when we release it:
  
  .. code-block:: python
  
-@@ -188,7 +210,7 @@
+-    version="0.1.5",
++    version="0.1.6",
      description="QuickWiki - Pylons 0.9.6 Tutorial application",
      url="http://wiki.pylonshq.com/display/pylonsdocs/QuickWiki+Tutorial",
  
  
  .. code-block:: ini
  
-@@ -202,20 +224,21 @@
+@@ -202,74 +227,61 @@
  
      $ python setup.py develop
  
  
 -    The command ``python setup.py develop`` installs your application in a special mode so that it behaves exactly as if it had been installed as an egg file by an end user. This is really useful when you are developing an application because it saves you having to create an egg and install it every time you want to test a change.
 +    The command :command:`python setup.py develop` installs your application in a special mode so that it behaves exactly as if it had been installed as an egg file by an end user. This is really useful when you are developing an application because it saves you having to create an egg and install it every time you want to test a change.
++
++.. _Storm: http://storm.canonical.com/
++.. _CouchDB: http://couchdb.apache.org/
++.. _Datastore: http://code.google.com/appengine/docs/datastore/
++.. _full and detailed documentation: http://www.sqlalchemy.org/docs/
++.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
  
  Configuration and Setup
  =======================
  
 -Now lets make the changes necessary to enable QuickWiki to be set up by an end user. First, open ``environment.py`` from the ``config`` directory of your project. After ``from pylons import config``, add the following import:
-+Now lets make the changes necessary to enable QuickWiki to be set up by an end user. First, open :file:`environment.py` from the :file:`config` directory of your project. After ``from pylons import config``, add the following import:
+-
+-.. code-block:: python
+-
+-    from sqlalchemy import engine_from_config
+-
+-Then, add this line at the end of the ``load_environment`` function:
+-
+-.. code-block:: python
+-
+-    config['pylons.g'].sa_engine = \
+-        engine_from_config(config, 'sqlalchemy.default.')
+-
+-This creates an **engine** for each instance of your application, which manages connections and is the base level at which SQLAlchemy communicates with the database. The engine is added to Pylons' ``config`` object, where you earlier saw it accessed in the ``base`` parameter for setting up SQLAlchemy's ``Session``.
+-
+-Now edit ``websetup.py``, used by the ``paster setup-app`` command, to look like this:
++Now lets make the changes necessary to enable QuickWiki to be set up by a user deploying your packaged application. Let's edit :file:`websetup.py`, used by the :command:`paster setup-app` command when an application is initially set up, to add some informative logging output and initialize data that the app needs out of the box:
  
  .. code-block:: python
  
-     from sqlalchemy import engine_from_config
+     """Setup the QuickWiki application"""
+     import logging
  
--Then, add this line at the end of the ``load_environment`` function:
-+TODO: is there a :func:?
-+Then, add this line at the end of the :meth:`load_environment` function:
+-    from paste.deploy import appconfig
+-    from pylons import config
+-
+     from quickwiki.config.environment import load_environment
  
- .. code-block:: python
+     log = logging.getLogger(__name__)
  
-@@ -224,7 +247,7 @@
+-    def setup_config(command, filename, section, vars):
++    def setup_app(command, conf, vars):
+         """Place any commands to setup quickwiki here"""
+-        conf = appconfig('config:' + filename)
+         load_environment(conf.global_conf, conf.local_conf)
  
- This creates an **engine** for each instance of your application, which manages connections and is the base level at which SQLAlchemy communicates with the database. The engine is added to Pylons' ``config`` object, where you earlier saw it accessed in the ``base`` parameter for setting up SQLAlchemy's ``Session``.
+-    # Populate the DB on 'paster setup-app'
+-    import quickwiki.model as model
++        # Load the models
++        from quickwiki.model import meta
++        meta.metadata.bind = meta.engine
  
--Now edit ``websetup.py``, used by the ``paster setup-app`` command, to look like this:
-+Now edit :file:`websetup.py`, used by the :command:`paster setup-app` command, to look like this:
+-    log.info("Setting up database connectivity...")
+-    engine = config['pylons.g'].sa_engine
+-    log.info("Creating tables...")
+-    model.metadata.create_all(bind=engine)
+-    log.info("Successfully set up.")
++        # Create the tables if they aren't there already
++        log.info("Creating tables...")
++        meta.metadata.create_all(checkfirst=True)
++        log.info("Successfully set up.")
  
- .. code-block:: python
- 
-@@ -260,16 +283,15 @@
-     model.Session.commit()
-     log.info("Successfully set up.")
+-    log.info("Adding front page data...")
+-    page = model.Page()
+-    page.title = 'FrontPage'
+-    page.content = 'Welcome to the QuickWiki front page.'
+-    model.Session.save(page)
+-    model.Session.commit()
+-    log.info("Successfully set up.")
++        import quickwiki.model as model
++        log.info("Adding front page data...")
++        page = model.Page()
++        page.title = u'FrontPage'
++        page.content = u'Welcome to the QuickWiki front page.'
++        meta.Session.add(page)
++        meta.Session.commit()
++        log.info("Successfully set up.")
  
 -You can see that ``environment.py``'s ``load_environment`` function is called, so our engine is ready and we can import the model. A SQLAlchemy ``MetaData`` object--which provides some utility methods for operating on database schema--usually needs to be connected to an engine, so the line ``model.metadata.create_all(bind=engine)`` uses the engine we've set up and, well, creates the table(s) we've defined. After the tables are created the other lines add some data for the simple front page to our wiki. Because we specified ``transactional=True`` when creating our ``Session``, operations will be wrapped in a transaction and committed atomically (unless your DB doesn't support transactions, like MySQL's default MyISAM tables -- but that's beyond the scope of this tutorial).
-+You can see that :file:`environment.py`'s :meth:`load_environment` function is called, so our engine is ready and we can import the model. A SQLAlchemy ``MetaData`` object--which provides some utility methods for operating on database schema--usually needs to be connected to an engine, so the line ``model.metadata.create_all(bind=engine)`` uses the engine we've set up and, well, creates the table(s) we've defined. After the tables are created the other lines add some data for the simple front page to our wiki. Because we specified ``transactional=True`` when creating our ``Session``, operations will be wrapped in a transaction and committed atomically (unless your DB doesn't support transactions, like MySQL's default MyISAM tables -- but that's beyond the scope of this tutorial).
++You can see that the :meth:`load_environment` function is called from :file:`config/environment.py`, which in turn sets up a database connection and session courtesy of code generated when you created your project. With these ready, we can import the model. A SQLAlchemy ``MetaData`` object--which provides some utility methods for operating on database schema--usually needs to be connected to an engine, so the line ``model.metadata.create_all(bind=engine)`` uses the engine we've set up and, well, creates the table(s) we've defined. After the tables are created the other lines add some data for the simple front page to our wiki. Because we specified ``transactional=True`` when creating our ``Session``, operations will be wrapped in a transaction and committed atomically (unless your DB doesn't support transactions, like MySQL's default MyISAM tables -- but that's beyond the scope of this tutorial).
  
 -To test this functionality run you first need to install your QuickWiki if you haven't already done so in order for ``paster`` to find the version we are developing instead of the version we installed at the very start:
 +To test this functionality run you first need to install your QuickWiki if you haven't already done so in order for :command:`paster` to find the version we are developing instead of the version we installed at the very start:
  .. code-block:: ini
  
      [app:main]
-@@ -280,7 +302,7 @@
+@@ -280,17 +292,17 @@
      # invalidate the URI when specifying a SQLite db via path name
      sqlalchemy.default.url = sqlite:///%(here)s/quickwiki.db
  
  
      See the SQLAlchemy note in the `Starting at the End`_ section for information on supported database URIs and a link to the SQLAlchemy documentation about the various options that can be included in them.
  
-@@ -290,7 +312,7 @@
+-If you want to see the SQL being generated, you can have SQLAlchemy echo it to the console by adding this line:
++If you want to see the SQL being generated, you can have :term:`SQLAlchemy` echo it to the console by adding this line:
+ 
+ .. code-block:: ini
  
      sqlalchemy.default.echo = true
  
  
  .. code-block:: bash
  
-@@ -298,24 +320,23 @@
+@@ -298,24 +310,23 @@
  
  At this stage you will need to ensure you have the appropriate Python database drivers for the database you chose, otherwise you might find SQLAlchemy complains it can't get the DBAPI module for the dialect it needs.
  
  
  .. code-block:: html+mako
  
-@@ -342,16 +363,17 @@
+@@ -342,16 +353,19 @@
          </body>
      </html>
  
 +All our other templates will be automatically inserted into the ``${next.body()}`` line and the whole page will be returned when we call the :meth:`render()` global from our controller so that we can easily apply a consistent theme to all our templates.
  
 -If you are interested in learning some of the features of Mako templates have a look at the comprehensive `Mako Documentation <http://www.makotemplates.org/docs/>`_. For now we just need to understand that next.body() is replaced with the child template and that anything within ``${...}`` brackets is executed and replaced with the result.
-+If you are interested in learning some of the features of Mako templates have a look at the comprehensive `Mako Documentation <http://www.makotemplates.org/docs/>`_. For now we just need to understand that ``next.body()`` is replaced with the child template and that anything within ``${...}`` brackets is executed and replaced with the result.
++If you are interested in learning some of the features of Mako templates have a look at the comprehensive `Mako Documentation`_. For now we just need to understand that ``next.body()`` is replaced with the child template and that anything within ``${...}`` brackets is executed and replaced with the result.
  
 -This ``base.mako`` also makes use of various helper functions attached to the ``h`` object. These are described in the `WebHelpers documentation <http://pylonshq.com/WebHelpers/module-index.html>`_. You can add more helpers to the ``h`` object by adding them to ``lib/helpers.py`` although for this project we don't need to do so.
 +TODO: clarify WebHelpers updates here
-+This :file:`base.mako` also makes use of various helper functions attached to the ``h`` object. These are described in the `WebHelpers documentation <http://pylonshq.com/WebHelpers/module-index.html>`_. You can add more helpers to the ``h`` object by adding them to ``lib/helpers.py`` although for this project we don't need to do so.
++This :file:`base.mako` also makes use of various helper functions attached to the ``h`` object. These are described in the :mod:`WebHelpers documentation <webhelpers>`. You can add more helpers to the ``h`` object by adding them to ``lib/helpers.py`` although for this project we don't need to do so.
++
++.. _Mako Documentation: http://www.makotemplates.org/docs/
  
  Routing
  =======
  
  .. code-block:: python
  
-@@ -360,7 +382,7 @@
+@@ -360,7 +374,7 @@
      map.connect(':title', controller='page', action='index', title='FrontPage')
      map.connect('*url', controller='template', action='view')
  
 -Note that the default route has been replaced. This tells Pylons to route the root URL ``/`` to the ``index()`` method of the ``PageController`` class in ``page.py`` and specify the ``title`` argument as ``FrontPage``. It also says that any URL of the form ``/SomePage`` should be routed to the same method but the ``title`` argument will contain the value of the first part of the URL, in this case ``SomePage``. Any other URLs which can't be matched by these maps are routed to the template controller as usual where they will result in a 404 error page being displayed.
-+Note that the default route has been replaced. This tells Pylons to route the root URL ``/`` to the :meth:`index()` method of the :class:`PageController` class in :file:`pages.py` and specify the ``title`` argument as ``FrontPage``. It also says that any URL of the form ``/SomePage`` should be routed to the same method but the ``title`` argument will contain the value of the first part of the URL, in this case ``SomePage``. Any other URLs which can't be matched by these maps are routed to the template controller as usual where they will result in a 404 error page being displayed.
++Note that the default route has been replaced. This tells Pylons to route the root URL ``/`` to the :meth:`index` method of the :class:`PageController` class in :file:`pages.py` and specify the ``title`` argument as ``FrontPage``. It also says that any URL of the form ``/SomePage`` should be routed to the same method but the ``title`` argument will contain the value of the first part of the URL, in this case ``SomePage``. Any other URLs which can't be matched by these maps are routed to the template controller as usual where they will result in a 404 error page being displayed.
  
  One of the main benefits of using the Routes system is that you can also create URLs automatically simply by specifying the routing arguments. For example if I want the URL for the page ``FrontPage`` I can create it with this code:
  
-@@ -375,29 +397,29 @@
+@@ -375,29 +389,30 @@
  Controllers
  ===========
  
  We are going to need the following actions:
  
 -``index(self, title)``
++TODO: fix meth formatting
 +:meth:`index(self, title)`
  displays a page based on the title
  
  deletes a page based on an AJAX drag and drop call
  
  Let's get cracking! We just need to make one quick preparation first: edit the ``BaseController`` class that your new page controller subclasses, so that we get a clean ``Session`` each time one of your controllers is called. Open ``lib/base.py`` and edit the ``__call__`` method like this:
-@@ -420,10 +442,10 @@
+@@ -420,10 +435,10 @@
  
  This is critical for avoiding unexpected and hard-to-debug behavior resulting from old session data between requests.
  
 -index()
 --------
-+:meth:`index()`
++:meth:`index`
 +---------------
  
 -Now we can get to work on the new controller in ``page.py``. First we'll import the Page class from our model class to save some typing later on. Add this line with the imports at the top of the file:
  
  .. code-block:: python
  
-@@ -431,7 +453,7 @@
+@@ -431,7 +446,7 @@
  
  This is also done the the ``base.py`` file for the Session class, as shown above. This is done sheerly for convenience, and you can instead choose to refer to ``model.Session`` and ``model.Page`` throughout your controllers, since ``BaseController`` imports the model for us. This may help to reduce confusion, especially in more complex applications.
  
 -On to the ``index`` method. Replace the existing ``index()`` action with this:
-+On to the ``index`` method. Replace the existing :meth:`index()` action with this:
++On to the ``index`` method. Replace the existing :meth:`index` action with this:
  
  .. code-block:: python
  
-@@ -445,7 +467,7 @@
+@@ -445,7 +460,7 @@
              return render('/new_page.mako')
          abort(404)
  
  
  .. code-block:: html+mako
  
-@@ -456,9 +478,11 @@
+@@ -456,9 +471,11 @@
  
  This template simply displays the page title and content.
  
  
  .. code-block:: html+mako
  
-@@ -475,9 +499,9 @@
+@@ -475,9 +492,9 @@
  
      $ paster serve --reload development.ini
  
  
  .. code-block:: css
  
-@@ -502,12 +526,12 @@
+@@ -502,12 +519,12 @@
      border-top: 1px solid #000;
      }
  
 -When you run the example you will notice that the word ``QuickWiki`` has been turned into a hyperlink by the ``get_wiki_content()`` method we added to our ``Page`` domain object earlier. You can click the link and will see an example of the new page screen from the ``new_page.mako`` template. If you follow the ``Create the page`` link you will see the Pylons automatic error handler kick in to tell you ``Action edit is not implemented``. Well, we better write it next, but before we do, have a play with the :ref:`interactive_debugging`, try clicking on the ``+`` or ``>>`` arrows and you will be able to interactively debug your application. It is a tremendously useful tool.
-+When you run the example you will notice that the word ``QuickWiki`` has been turned into a hyperlink by the :meth:`get_wiki_content()` method we added to our :class:`Page` domain object earlier. You can click the link and will see an example of the new page screen from the :file:`new.mako` template. If you follow the ``Create the page`` link you will see the Pylons automatic error handler kick in to tell you ``Action edit is not implemented``. Well, we better write it next, but before we do, have a play with the :ref:`interactive_debugging`, try clicking on the ``+`` or ``>>`` arrows and you will be able to interactively debug your application. It is a tremendously useful tool.
++When you run the example you will notice that the word ``QuickWiki`` has been turned into a hyperlink by the :meth:`get_wiki_content` method we added to our :class:`Page` domain object earlier. You can click the link and will see an example of the new page screen from the :file:`new.mako` template. If you follow the ``Create the page`` link you will see the Pylons automatic error handler kick in to tell you ``Action edit is not implemented``. Well, we better write it next, but before we do, have a play with the :ref:`interactive_debugging`, try clicking on the ``+`` or ``>>`` arrows and you will be able to interactively debug your application. It is a tremendously useful tool.
  
 -edit()
 -------
  
  .. code-block:: python
  
-@@ -518,7 +542,7 @@
+@@ -518,7 +535,7 @@
              c.content = page.content
          return render('/edit.mako')
  
  
  .. code-block:: html+mako
  
-@@ -531,16 +555,18 @@
+@@ -531,16 +548,18 @@
      ${h.submit(value="Save changes", name='commit')}
      ${h.end_form()}
  
  
 -save()
 -------
-+We are making use of the ``h`` object to create our form and field objects. This saves a bit of manual HTML writing. The form submits to the :meth:`save` action to save the new or updated content so let's write that next.
++We are making use of the :obj:`h` object to create our form and field objects. This saves a bit of manual HTML writing. The form submits to the :meth:`save` action to save the new or updated content so let's write that next.
  
 -The first thing the ``save()`` action has to do is to see if the page being saved already exists. If not it creates it with ``page = model.Page()``. Next it needs the updated content. In Pylons you can get request parameters from form submissions via GET and POST requests from the appropriately named ``request.params`` object. For form submissions from *only* GET or POST requests, use ``request.GET`` or ``request.POST``. Only POST requests should generate side effects (like changing data), so the save action will reference ``request.POST`` for the parameters.
 +:meth:`save`
  
  .. code-block:: python
  
-@@ -558,10 +584,11 @@
+@@ -558,10 +577,11 @@
          Session.commit()
          return render('/page.mako')
  
  
  .. code-block:: html+mako
  
-@@ -569,7 +596,7 @@
+@@ -569,7 +589,7 @@
      <p><div id="message">${c.message}</div></p>
      % endif
  
  
  .. code-block:: css
  
-@@ -577,16 +604,16 @@
+@@ -577,16 +597,16 @@
          color: orangered;
      }
  
  
 -list()
 -------
-+:meth:`list()`
++:meth:`list`
 +--------------
  
 -Add the ``list()`` action:
-+Add the :meth:`list()` action:
++Add the :meth:`list` action:
  
  .. code-block:: python
  
-@@ -594,7 +621,7 @@
+@@ -594,7 +614,7 @@
          c.titles = [page.title for page in Session.query(Page).all()]
          return render('/list.mako')
  
 -The ``list()`` action simply gets all the pages from the database. Create the ``templates/list.mako`` file to display the list:
-+The :meth:`list()` action simply gets all the pages from the database. Create the :file:`templates/pages/list.mako` file to display the list:
++The :meth:`list` action simply gets all the pages from the database. Create the :file:`templates/pages/list.mako` file to display the list:
  
  .. code-block:: html+mako
  
-@@ -610,7 +637,7 @@
+@@ -610,7 +630,7 @@
      % endfor
      </ul>
  
  
  .. code-block:: html+mako
  
-@@ -644,14 +671,14 @@
+@@ -644,24 +664,26 @@
      | ${h.link_to('Title List', h.url_for(action='list', title=None))}
      </%def>
  
  
 -delete()
 ---------
-+:meth:`delete()`
++:meth:`delete`
 +----------------
  
  Since this tutorial is designed to get you familiar with as much of Pylons core functionality as possible we will use some AJAX to allow the user to drag a title from the title list into a trash area that will automatically delete the page.
  
-@@ -661,7 +688,7 @@
+-Add this line to ``templates/base.mako`` before ``</head>``:
++Add this line to :file:`templates/base.mako` before ``</head>``:
+ 
+ .. code-block:: mako
  
      ${h.javascript_include_tag('/javascripts/effects.js', builtins=True)}
  
 -.. Note:: The ``h.javascript_include_tag()`` helper will create links to all the built-in JavaScripts we need and also add ``/javascripts/effects.js`` creating HTML that looks like this when you access it from a browser:
-+.. note:: The ``h.javascript_include_tag()`` helper will create links to all the built-in JavaScripts we need and also add ``/javascripts/effects.js`` creating HTML that looks like this when you access it from a browser:
++.. note::
++
++    The ``h.javascript_include_tag()`` helper will create links to all the built-in JavaScripts we need and also add ``/javascripts/effects.js`` creating HTML that looks like this when you access it from a browser:
  
  .. code-block:: html
  
-@@ -702,9 +729,11 @@
+@@ -702,9 +724,11 @@
      </li>
      % endfor
  
 +.. note::
  
 -We've also added the ``<span>`` tags, and marked each of the titles as a draggable element that reverts to its original position if it isn't dropped over a drop target. If we want to be able to delete the pages we better add a drop target. Try it out at http://127.0.0.1:5000/page/list by dragging the titles themselves around the screen. Notice how much functionality we get with just the one helper ``h.draggable_element()``.
-+    You can see that we've moved the ``for`` loop into the new template. This is so that we can easily call ``render()`` to update it via AJAX from the delete action that we'll add to our controller in just a moment. We ``<%include />`` this new template in the original ``list.mako``; this is a lot like ``<%inherit />``, but moving downward hierarchically instead of upward. It's perhaps the most basic of templating functions and is much like ``include`` in PHP templating, for example. Notice that ``list-titles.mako`` does not inherit from ``base.mako`` like the others we've created. This way we take maximal advantage of Mako's inheritance, while further reducing code duplication with ``<%include />``.
++    You can see that we've moved the ``for`` loop into the new template. This is so that we can easily call :meth:`render` to update it via AJAX from the delete action that we'll add to our controller in just a moment. We ``<%include />`` this new template in the original ``list.mako``; this is a lot like ``<%inherit />``, but moving downward hierarchically instead of upward. It's perhaps the most basic of templating functions and is much like ``include`` in PHP templating, for example. Notice that ``list-titles.mako`` does not inherit from ``base.mako`` like the others we've created. This way we take maximal advantage of Mako's inheritance, while further reducing code duplication with ``<%include />``.
 +
 +We've also added the ``<span>`` tags, and marked each of the titles as a draggable element that reverts to its original position if it isn't dropped over a drop target. If we want to be able to delete the pages we better add a drop target. Try it out at http://localhost:5000/page/list by dragging the titles themselves around the screen. Notice how much functionality we get with just the one helper ``h.draggable_element()``.
  
  We better have somewhere to drop the titles to delete them, so add this before the ``<ul id="titles">`` line in ``templates/list.mako`` :
  
-@@ -727,7 +756,9 @@
+@@ -727,7 +751,9 @@
      padding: 15px;
      }
  
  
  When a title is dropped on the ``trash`` box an AJAX request will be made to the ``delete()`` action, posting an ``id`` parameter with the ``id`` of the element that was dropped. The element with ``id`` ``titles`` will be updated with whatever is returned from the action, so we better add a ``delete()`` action that returns the new list of titles excluding the one that has been deleted:
  
-@@ -744,9 +775,9 @@
+@@ -744,9 +770,9 @@
  
  The title of the page is obtained from the ``id`` element and the object is loaded and then deleted. The change is saved with ``model.Session.commit()`` before the list of remaining titles is re-rendered by the template ``templates/list-titles.mako``.
  
  
  Publishing the Finished Product
  ===============================
-@@ -757,11 +788,11 @@
+@@ -757,11 +783,11 @@
  
      $ python setup.py bdist_egg
  
  
  You should probably make eggs for each version of Python your users might require by running the above commands with both Python 2.4 and 2.5 to create both versions of the eggs.
  
-@@ -771,13 +802,17 @@
+@@ -771,13 +797,17 @@
  
      $ python setup.py register
  
 -.. Note:: A `CheeseShop Tutorial <http://wiki.python.org/moin/CheeseShopTutorial>`_ has been written and `full documentation on setup.py <http://docs.python.org/dist/dist.html>`_ is available from the Python website. You can even use `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ in the ``description`` and ``long_description`` areas of ``setup.py`` to add formatting to the pages produced on the CheeseShop. There is also `another tutorial here <http://www.python.org/~jeremy/weblog/030924.html>`_.
 +.. note::
 +
-+    A `CheeseShop Tutorial <http://wiki.python.org/moin/CheeseShopTutorial>`_ has been written and `full documentation on setup.py <http://docs.python.org/dist/dist.html>`_ is available from the Python website. You can even use `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ in the ``description`` and ``long_description`` areas of :file:`setup.py` to add formatting to the pages produced on the CheeseShop. There is also `another tutorial here <http://www.python.org/~jeremy/weblog/030924.html>`_.
++    A `CheeseShop Tutorial`_ has been written and `full documentation on setup.py`_ is available from the Python website. You can even use `reStructuredText`_ in the ``description`` and ``long_description`` areas of :file:`setup.py` to add formatting to the pages produced on the CheeseShop.
  
  Finally you can sign in to the CheeseShop with the account details you used when you registered your application and upload the eggs you've created. If that seems too difficult you can even use this command which should be run for each version of Python supported to upload the eggs for you:
  
-@@ -785,7 +820,7 @@
+@@ -785,7 +815,7 @@
  
      $ python setup.py bdist_egg upload
  
 -Before this will work you will need to create a ``.pypirc`` file in your home directory containing your username and password so that the ``upload`` command knows who to sign in as. It should look similar to this:
-+Before this will work you will need to create a :file:`.pypirc` file in your home directory containing your username and password so that the :command:`upload`` command knows who to sign in as. It should look similar to this:
++Before this will work you will need to create a :file:`.pypirc` file in your home directory containing your username and password so that the :command:`upload` command knows who to sign in as. It should look similar to this:
  
  .. code-block:: ini
  
-@@ -793,22 +828,26 @@
+@@ -793,31 +823,41 @@
      username: james
      password: password
  
 -.. Tip:: This works on windows too but you will need to set your ``HOME`` environment variable first. If your home directory is ``C:\Documents and Settings\James`` you would put your ``.pypirc`` file in that directory and set your ``HOME`` environment variable with this command:
 +.. tip::
 +
-+    This works on windows too but you will need to set your ``HOME`` environment variable first. If your home directory is ``C:\Documents and Settings\James`` you would put your :file:`.pypirc` file in that directory and set your ``HOME`` environment variable with this command:
++    This works on Windows too, but you will need to set your :envvar:`HOME` environment variable first. If your home directory is :file:`C:\Documents and Settings\James` you would put your :file:`.pypirc` file in that directory and set your :envvar:`HOME` environment variable with this command:
  
  .. code-block:: bash
  
  
 -Now that the application is on CheeseShop anyone can install it with the ``easy_install`` command exactly as we did right at the very start of this tutorial.
 +Now that the application is on CheeseShop anyone can install it with the :command:`easy_install` command exactly as we did right at the very start of this tutorial.
++
++.. _CheeseShop Tutorial: http://wiki.python.org/moin/CheeseShopTutorial
++.. _full documentation on setup.py: http://docs.python.org/dist/dist.html
++.. _reStructuredText: http://docutils.sourceforge.net/rst.html
  
  Security
  ========
  
 -.. Danger:: Always set ``debug = false`` in configuration files for production sites and make sure your users do to.
 +.. warning::
+ 
+-You should NEVER run a production site accessible to the public with debug mode on. If there was a problem with your application and an interactive error page was shown, the visitor would be able to run any Python commands they liked in the same way you can when you are debugging. This would obviously allow them to do all sorts of malicious things so it is very important you turn off interactive debugging for production sites by setting ``debug = false`` in configuration files and also that you make users of your software do the same.
++    Always set ``debug = false`` in configuration files for production sites and make sure your users do too.
 +
-+    Always set ``debug = false`` in configuration files for production sites and make sure your users do too.
++You should **never** run a production site accessible to the public with debug mode on. If there was a problem with your application and an interactive error page was shown, the visitor would be able to run any Python commands they liked in the same way you can when you are debugging. This would obviously allow them to do all sorts of malicious things so it is very important you turn off interactive debugging for production sites by setting ``debug = false`` in configuration files and also that you make users of your software do the same.
  
- You should NEVER run a production site accessible to the public with debug mode on. If there was a problem with your application and an interactive error page was shown, the visitor would be able to run any Python commands they liked in the same way you can when you are debugging. This would obviously allow them to do all sorts of malicious things so it is very important you turn off interactive debugging for production sites by setting ``debug = false`` in configuration files and also that you make users of your software do the same.
+ Summary
+ =======
  
+ We've gone through the whole cycle of creating and distributing a Pylons application looking at setup and configuration, routing, models, controllers and templates. Hopefully you have an idea of how powerful Pylons is and, once you get used to the concepts introduced in this tutorial, how easy it is to create sophisticated, distributable applications with Pylons.
+ 
+-That's it, I hope you found the tutorial useful. You are encouraged to email any comments to the `Pylons mailing list <http://groups.google.co.uk/group/pylons-discuss>`_ where they will be gratefully received.
++That's it, I hope you found the tutorial useful. You are encouraged to email any comments to the `Pylons mailing list`_ where they will be gratefully received.
++
++.. _Pylons mailing list: http://groups.google.com/group/pylons-discuss
+ 
+ ToDo
+ ====