summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/tutorials/wiki2/authorization.rst115
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst8
-rw-r--r--docs/tutorials/wiki2/definingmodels.rst37
-rw-r--r--docs/tutorials/wiki2/definingviews.rst35
-rw-r--r--docs/tutorials/wiki2/distributing.rst5
-rw-r--r--docs/tutorials/wiki2/installation.rst32
-rw-r--r--docs/tutorials/wiki2/src/authorization/MANIFEST.in2
-rw-r--r--docs/tutorials/wiki2/src/authorization/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py11
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py72
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py33
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py19
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py27
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/security/default.py11
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja28
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja26
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja26
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/tests.py18
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/default.py54
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py5
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/models/MANIFEST.in2
-rw-r--r--docs/tutorials/wiki2/src/models/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models/__init__.py72
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models/meta.py33
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py3
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py27
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja28
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/tests.py18
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/views/default.py4
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/views/errors.py5
-rw-r--r--docs/tutorials/wiki2/src/tests/MANIFEST.in2
-rw-r--r--docs/tutorials/wiki2/src/tests/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/tests/setup.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/__init__.py11
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py72
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/models/meta.py33
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py19
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py27
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/security/default.py11
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja28
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja26
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja26
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py29
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py46
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/views/default.py54
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/views/errors.py5
-rw-r--r--docs/tutorials/wiki2/src/views/MANIFEST.in2
-rw-r--r--docs/tutorials/wiki2/src/views/production.ini2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models/__init__.py72
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models/meta.py33
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py3
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py27
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja28
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja22
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja22
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/tests.py18
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views/default.py23
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/views/errors.py5
-rw-r--r--docs/tutorials/wiki2/tests.rst8
65 files changed, 683 insertions, 542 deletions
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index e40433497..1ee5cc714 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -42,7 +42,7 @@ Access control
Add users and groups
~~~~~~~~~~~~~~~~~~~~
-Create a new ``tutorial/tutorial/security/default.py`` subpackage with the
+Create a new ``tutorial/security/default.py`` subpackage with the
following content:
.. literalinclude:: src/authorization/tutorial/security/default.py
@@ -68,21 +68,17 @@ database, but here we use "dummy" data to represent user and groups sources.
Add an ACL
~~~~~~~~~~
-Open ``tutorial/tutorial/models/mymodel.py`` and add the following import
-statement just after the ``Base`` import at the top:
+Open ``tutorial/models/mymodel.py`` and add the following import
+statement at the top:
.. literalinclude:: src/authorization/tutorial/models/mymodel.py
- :lines: 3-6
- :linenos:
- :lineno-start: 3
+ :lines: 1-4
:language: python
Add the following class definition at the end:
.. literalinclude:: src/authorization/tutorial/models/mymodel.py
- :lines: 22-26
- :linenos:
- :lineno-start: 22
+ :lines: 22-29
:language: python
We import :data:`~pyramid.security.Allow`, an action that means that
@@ -100,13 +96,13 @@ need to associate it to our :app:`Pyramid` application, so the ACL is provided
to each view in the :term:`context` of the request as the ``context``
attribute.
-Open ``tutorial/tutorial/__init__.py`` and add a ``root_factory`` parameter to
-our :term:`Configurator` constructor, that points to the class we created
-above:
+Open ``tutorial/__init__.py`` and define a new root factory using
+:meth:`pyramid.config.Configurator.set_root_factory` using the class that we
+created above:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 13-14
- :emphasize-lines: 2
+ :lines: 14-17
+ :emphasize-lines: 17
:language: python
Only the highlighted line needs to be added.
@@ -122,22 +118,19 @@ for more information about what an :term:`ACL` represents.
Add authentication and authorization policies
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/tutorial/__init__.py`` and add the highlighted import
+Open ``tutorial/__init__.py`` and add the highlighted import
statements:
.. literalinclude:: src/authorization/tutorial/__init__.py
:lines: 1-5
- :linenos:
:emphasize-lines: 2-5
:language: python
Now add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 7-16
- :linenos:
- :lineno-start: 7
- :emphasize-lines: 4-6,9-10
+ :lines: 11-19
+ :emphasize-lines: 1-3,8-9
:language: python
Only the highlighted lines need to be added.
@@ -157,17 +150,17 @@ machinery represented by this policy; it is required. The ``callback`` is the
Add permission declarations
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/tutorial/views/default.py`` and add a ``permission='view'``
+Open ``tutorial/views/default.py`` and add a ``permission='view'``
parameter to the ``@view_config`` decorator for ``view_wiki()`` and
``view_page()`` as follows:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 27-29
- :emphasize-lines: 1-2
+ :lines: 24-25
+ :emphasize-lines: 1
:language: python
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 33-35
+ :lines: 29-31
:emphasize-lines: 1-2
:language: python
@@ -180,12 +173,12 @@ Add a ``permission='edit'`` parameter to the ``@view_config`` decorators for
``add_page()`` and ``edit_page()``:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 57-59
+ :lines: 52-54
:emphasize-lines: 1-2
:language: python
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 72-74
+ :lines: 66-68
:emphasize-lines: 1-2
:language: python
@@ -203,11 +196,11 @@ Login, logout
Add routes for /login and /logout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Go back to ``tutorial/tutorial/__init__.py`` and add these two routes as
+Go back to ``tutorial/__init__.py`` and add these two routes as
highlighted:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 20-23
+ :lines: 21-24
:emphasize-lines: 2-3
:language: python
@@ -215,7 +208,7 @@ highlighted:
``view_page`` route definition:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 23
+ :lines: 24
:language: python
This is because ``view_page``'s route definition uses a catch-all
@@ -235,12 +228,12 @@ 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.
-Add the following import statements to ``tutorial/tutorial/views/default.py``
+Add the following import statements to ``tutorial/views/default.py``
after the import from ``pyramid.httpexceptions``:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 10-20
- :emphasize-lines: 1-11
+ :lines: 9-19
+ :emphasize-lines: 1-8,11
:language: python
All the highlighted lines need to be added or edited.
@@ -253,7 +246,7 @@ cookie.
Now add the ``login`` and ``logout`` views at the end of the file:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 88-121
+ :lines: 81-112
:language: python
``login()`` has two decorators:
@@ -274,7 +267,7 @@ it with the ``logout`` route. It will be invoked when we visit ``/logout``.
Add the ``login.jinja2`` template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Create ``tutorial/tutorial/templates/login.jinja2`` with the following content:
+Create ``tutorial/templates/login.jinja2`` with the following content:
.. literalinclude:: src/authorization/tutorial/templates/login.jinja2
:language: html
@@ -282,38 +275,11 @@ Create ``tutorial/tutorial/templates/login.jinja2`` with the following content:
The above template is referenced in the login view that we just added in
``views/default.py``.
-Return a ``logged_in`` flag to the renderer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Open ``tutorial/tutorial/views/default.py`` again. Add a ``logged_in``
-parameter to the return value of ``view_page()``, ``add_page()``, and
-``edit_page()`` as follows:
-
-.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 54-55
- :emphasize-lines: 1-2
- :language: python
-
-.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 69-70
- :emphasize-lines: 1-2
- :language: python
-
-.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 82-86
- :emphasize-lines: 3-4
- :language: python
-
-Only the highlighted lines need to be added or edited.
-
-The :meth:`pyramid.request.Request.authenticated_userid` will be ``None`` if
-the user is not authenticated, or a userid if the user is authenticated.
-
Add a "Logout" link when logged in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Open ``tutorial/tutorial/templates/edit.jinja2`` and
-``tutorial/tutorial/templates/view.jinja2`` and add the following code as
+Open ``tutorial/templates/edit.jinja2`` and
+``tutorial/templates/view.jinja2`` and add the following code as
indicated by the highlighted lines.
.. literalinclude:: src/authorization/tutorial/templates/edit.jinja2
@@ -321,42 +287,41 @@ indicated by the highlighted lines.
:emphasize-lines: 3-7
:language: html
-The attribute ``logged_in`` will make the element be included when
-``logged_in`` is any user id. The link will invoke the logout view. The above
-element will not be included if ``logged_in`` is ``None``, such as when a user
-is not authenticated.
+The :meth:`pyramid.request.Request.authenticated_userid` will be ``None`` if
+the user is not authenticated, or a userid if the user is authenticated. This
+check will make the logout link active only when the user is logged in.
Reviewing our changes
---------------------
-Our ``tutorial/tutorial/__init__.py`` will look like this when we're done:
+Our ``tutorial/__init__.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
- :emphasize-lines: 2-3,5,10-12,14-16,21-22
+ :emphasize-lines: 2-3,5,11-13,17-19,22-23
:language: python
Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/models/mymodel.py`` will look like this when we're done:
+Our ``tutorial/models/mymodel.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/models/mymodel.py
:linenos:
- :emphasize-lines: 3-6,22-26
+ :emphasize-lines: 1-4,22-29
:language: python
Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/views/default.py`` will look like this when we're done:
+Our ``tutorial/views/default.py`` will look like this when we're done:
.. literalinclude:: src/authorization/tutorial/views/default.py
:linenos:
- :emphasize-lines: 10-20,27-28,33-34,54-55,57-58,69-70,72-73,84-85,88-121
+ :emphasize-lines: 9-16,19,24,29-30,52-53,66-67,81-112
:language: python
Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/edit.jinja2`` template will look like this when
+Our ``tutorial/templates/edit.jinja2`` template will look like this when
we're done:
.. literalinclude:: src/authorization/tutorial/templates/edit.jinja2
@@ -366,7 +331,7 @@ we're done:
Only the highlighted lines need to be added or edited.
-Our ``tutorial/tutorial/templates/view.jinja2`` template will look like this when
+Our ``tutorial/templates/view.jinja2`` template will look like this when
we're done:
.. literalinclude:: src/authorization/tutorial/templates/view.jinja2
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 3533bb455..485d38047 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -16,7 +16,7 @@ package. We use ``__init__.py`` both as a marker, indicating the directory in
which it's contained is a package, and to contain application configuration
code.
-Open ``tutorial/tutorial/__init__.py``. It should already contain the
+Open ``tutorial/__init__.py``. It should already contain the
following:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
@@ -134,7 +134,7 @@ The main function of a web framework is mapping each URL pattern to code (a
corresponding :term:`route`. Our application uses the
:meth:`pyramid.view.view_config` decorator to perform this mapping.
-Open ``tutorial/tutorial/views/default.py`` in the ``views`` package. It
+Open ``tutorial/views/default.py`` in the ``views`` package. It
should already contain the following:
.. literalinclude:: src/basiclayout/tutorial/views/default.py
@@ -179,7 +179,7 @@ In a SQLAlchemy-based application, a *model* object is an object composed by
querying the SQL database. The ``models`` package is where the ``alchemy``
scaffold put the classes that implement our models.
-First, open ``tutorial/tutorial/models/meta.py``, which should already contain
+First, open ``tutorial/models/meta.py``, which should already contain
the following:
.. literalinclude:: src/basiclayout/tutorial/models/meta.py
@@ -234,7 +234,7 @@ The ``MyModel`` class has a ``__tablename__`` attribute. This informs
SQLAlchemy which table to use to store the data representing instances of this
class.
-Finally, open ``tutorial/tutorial/models/__init__.py``, which should already
+Finally, open ``tutorial/models/__init__.py``, which should already
contain the following:
.. literalinclude:: src/basiclayout/tutorial/models/__init__.py
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst
index b38177d04..b90bf77e6 100644
--- a/docs/tutorials/wiki2/definingmodels.rst
+++ b/docs/tutorials/wiki2/definingmodels.rst
@@ -3,7 +3,7 @@ Defining the Domain Model
=========================
The first change we'll make to our stock ``pcreate``-generated application will
-be to define a :term:`domain model` constructor representing a wiki page.
+be to define a wiki page :term:`domain model`.
We'll do this inside our ``mymodel.py`` file.
@@ -12,19 +12,20 @@ Edit ``mymodel.py``
.. note::
- There is nothing special about the filename ``mymodel.py``. A
- project may have many models throughout its codebase in arbitrarily named
- files. Files implementing models often have ``model`` in their filenames
- or they may live in a Python subpackage of your application package named
- ``models`` (as we've done in this tutorial), but this is only by convention.
+ There is nothing special about the filename ``mymodel.py`` except that it
+ is a Python module. A project may have many models throughout its codebase
+ in arbitrarily named modules. Modules implementing models often have
+ ``model`` in their names or they may live in a Python subpackage of your
+ application package named ``models`` (as we've done in this tutorial), but
+ this is only a convention and not a requirement.
-Open the ``tutorial/tutorial/models/mymodel.py`` file and edit it to look like
+Open the ``tutorial/models/mymodel.py`` file and edit it to look like
the following:
.. literalinclude:: src/models/tutorial/models/mymodel.py
:linenos:
:language: py
- :emphasize-lines: 9-11,13,14
+ :emphasize-lines: 10-12,14-15
The highlighted lines are the ones that need to be changed, as well as
removing lines that reference ``Index``.
@@ -33,7 +34,7 @@ The first thing we've done is remove the stock ``MyModel`` class
from the generated ``models.py`` file. The ``MyModel`` class is only a
sample and we're not going to use it.
-Then we added a ``Page`` class. Because this is an SQLAlchemy application,
+Then we added a ``Page`` class. Because this is a SQLAlchemy application,
this class inherits from an instance of
:func:`sqlalchemy.ext.declarative.declarative_base`.
@@ -42,7 +43,7 @@ this class inherits from an instance of
:linenos:
:language: python
-As you can see, our ``Page`` class has a class level attribute
+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``, ``name``,
@@ -57,15 +58,15 @@ Edit ``models/__init__.py``
---------------------------
Since we are using a package for our models, we also need to update our
-``__init__.py`` file.
+``__init__.py`` file to ensure that the model is attached to the metadata.
-Open the ``tutorial/tutorial/models/__init__.py`` file and edit it to look like
+Open the ``tutorial/models/__init__.py`` file and edit it to look like
the following:
.. literalinclude:: src/models/tutorial/models/__init__.py
:linenos:
:language: py
- :emphasize-lines: 4
+ :emphasize-lines: 8
Here we need to align our import with the name of the model ``Page``.
@@ -82,19 +83,17 @@ Since we've changed our model, we need to make changes to our
``initializedb.py`` script. In particular, we'll replace our import of
``MyModel`` with one of ``Page`` and we'll change the very end of the script
to create a ``Page`` rather than a ``MyModel`` and add it to our
-``DBSession``.
+``dbsession``.
-Open ``tutorial/tutorial/scripts/initializedb.py`` and edit it to look like
+Open ``tutorial/scripts/initializedb.py`` and edit it to look like
the following:
.. literalinclude:: src/models/tutorial/scripts/initializedb.py
:linenos:
:language: python
- :emphasize-lines: 16,31,41
+ :emphasize-lines: 18,44-45
-Only the highlighted lines need to be changed, as well as removing the lines
-referencing ``pyramid.scripts.common`` and ``options`` under the ``main``
-function.
+Only the highlighted lines need to be changed.
Installing the project and re-initializing the database
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index 08fa8f16b..4bc7f461b 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -67,7 +67,7 @@ like this::
Adding view functions in ``views/default.py``
=============================================
-It's time for a major change. Open ``tutorial/tutorial/views/default.py`` and
+It's time for a major change. Open ``tutorial/views/default.py`` and
edit it to look like the following:
.. literalinclude:: src/views/tutorial/views/default.py
@@ -96,11 +96,12 @@ We'll describe each one briefly in the following sections.
.. note::
- There is nothing special about the filename ``default.py``. A project may
- have many view callables throughout its codebase in arbitrarily named files.
- Files implementing view callables often have ``view`` in their filenames (or
+ There is nothing special about the filename ``default.py`` exept that
+ it is a Python module. A project may have many view callables throughout
+ its codebase in arbitrarily named modules.
+ Modules implementing view callables often have ``view`` in their name (or
may live in a Python subpackage of your application package named ``views``,
- as in our case), but this is only by convention.
+ as in our case), but this is only by convention, not a requirement.
The ``view_wiki`` view function
-------------------------------
@@ -109,7 +110,7 @@ Following is the code for the ``view_wiki`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views/default.py
:lines: 17-20
- :lineno-start: 17
+ :lineno-match:
:linenos:
:language: python
@@ -119,8 +120,8 @@ represents the path to our "FrontPage".
The ``view_wiki`` view callable always redirects to the URL of a Page resource
named "FrontPage". To do so, it returns an instance of the
-:class:`pyramid.httpexceptions.HTTPFound` class (instances of which implement
-the :class:`pyramid.interfaces.IResponse` interface, like
+:class:`pyramid.httpexceptions.HTTPFound` class (instances of which
+implement the :class:`pyramid.interfaces.IResponse` interface, like
:class:`pyramid.response.Response` does). It uses the
:meth:`pyramid.request.Request.route_url` API to construct an URL to the
``FrontPage`` page (i.e., ``http://localhost:6543/FrontPage``), and uses it as
@@ -133,7 +134,7 @@ Here is the code for the ``view_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views/default.py
:lines: 22-42
- :lineno-start: 22
+ :lineno-match:
:linenos:
:language: python
@@ -159,7 +160,7 @@ template, and we return a dictionary with a number of arguments. The fact that
``view_page()`` returns a dictionary (as opposed to a :term:`response` object)
is a cue to :app:`Pyramid` that it should try to use a :term:`renderer`
associated with the view configuration to render a response. In our case, the
-renderer used will be the ``templates/view.jinja2`` template, as indicated in
+renderer used will be the ``view.jinja2`` template, as indicated in
the ``@view_config`` decorator that is applied to ``view_page()``.
The ``add_page`` view function
@@ -169,7 +170,7 @@ Here is the code for the ``add_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views/default.py
:lines: 44-55
- :lineno-start: 44
+ :lineno-match:
:linenos:
:language: python
@@ -209,8 +210,8 @@ The ``edit_page`` view function
Here is the code for the ``edit_page`` view function and its decorator:
.. literalinclude:: src/views/tutorial/views/default.py
- :lines: 57-69
- :lineno-start: 57
+ :lines: 57-68
+ :lineno-match:
:linenos:
:language: python
@@ -243,7 +244,7 @@ as such.
The ``view.jinja2`` template
----------------------------
-Create ``tutorial/tutorial/templates/view.jinja2`` and add the following
+Create ``tutorial/templates/view.jinja2`` and add the following
content:
.. literalinclude:: src/views/tutorial/templates/view.jinja2
@@ -263,7 +264,7 @@ wiki page. It includes:
The ``edit.jinja2`` template
----------------------------
-Create ``tutorial/tutorial/templates/edit.jinja2`` and add the following
+Create ``tutorial/templates/edit.jinja2`` and add the following
content:
.. literalinclude:: src/views/tutorial/templates/edit.jinja2
@@ -281,7 +282,9 @@ editing a wiki page. It displays a page containing a form that includes:
The form POSTs back to the ``save_url`` argument supplied by the view (line
42). The view will use the ``body`` and ``form.submitted`` values.
-.. note:: Our templates use a ``request`` object that none of our tutorial
+.. note::
+
+ Our templates use a ``request`` object that none of our tutorial
views return in their dictionary. ``request`` is one of several names that
are available "by default" in a template when a template renderer is used.
See :ref:`renderer_system_values` for information about other names that
diff --git a/docs/tutorials/wiki2/distributing.rst b/docs/tutorials/wiki2/distributing.rst
index fee50a1cf..ec90859a9 100644
--- a/docs/tutorials/wiki2/distributing.rst
+++ b/docs/tutorials/wiki2/distributing.rst
@@ -4,9 +4,8 @@ Distributing Your Application
Once your application works properly, you can create a "tarball" from it by
using the ``setup.py sdist`` command. The following commands assume your
-current working directory is the ``tutorial`` package we've created and that
-the parent directory of the ``tutorial`` package is a virtualenv representing
-a :app:`Pyramid` environment.
+current working directory contains the ``tutorial`` package and the
+``setup.py`` file.
On UNIX:
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index 047c66c06..5d6d8e56b 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -298,6 +298,13 @@ Initializing the database
We need to use the ``initialize_tutorial_db`` :term:`console
script` to initialize our database.
+.. note::
+
+ The ``initialize_tutorial_db`` command is not performing a migration but
+ rather simply creating missing tables and adding some dummy data. If you
+ already have a database, you should delete it before running
+ ``initialize_tutorial_db`` again.
+
Type the following command, making sure you are still in the ``tutorial``
directory (the directory with a ``development.ini`` in it):
@@ -397,12 +404,17 @@ Decisions the ``alchemy`` scaffold has made for you
Creating a project using the ``alchemy`` scaffold makes the following
assumptions:
-- you are willing to use :term:`SQLAlchemy` as a database access tool
+- You are willing to use :term:`SQLAlchemy` as a database access tool.
+
+- You are willing to use :term:`URL dispatch` to map URLs to code.
-- you are willing to use :term:`URL dispatch` to map URLs to code
+- You want to use zope.sqlalchemy_, pyramid_tm_ and the transaction_ package
+ to scope sessions to requests.
-- you want to use ``zope.sqlalchemy`` and ``pyramid_tm`` to scope
- sessions to requests
+- You want to use pyramid_jinja2_ to render your templates.
+ Different templating engines can be used but we had to choose one to
+ make the tutorial. See :ref:`available_template_system_bindings` for some
+ options.
.. note::
@@ -411,3 +423,15 @@ assumptions:
mechanism to map URLs to code (:term:`traversal`). However, for the
purposes of this tutorial, we'll only be using URL dispatch and
SQLAlchemy.
+
+.. _pyramid_jinja2:
+ http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/
+
+.. _pyramid_tm:
+ http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/
+
+.. _zope.sqlalchemy:
+ https://pypi.python.org/pypi/zope.sqlalchemy
+
+.. _transaction:
+ http://zodb.readthedocs.org/en/latest/transactions.html
diff --git a/docs/tutorials/wiki2/src/authorization/MANIFEST.in b/docs/tutorials/wiki2/src/authorization/MANIFEST.in
index 81beba1b1..42cd299b5 100644
--- a/docs/tutorials/wiki2/src/authorization/MANIFEST.in
+++ b/docs/tutorials/wiki2/src/authorization/MANIFEST.in
@@ -1,2 +1,2 @@
include *.txt *.ini *.cfg *.rst
-recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
+recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini
index 97acfbd7d..cb1db3211 100644
--- a/docs/tutorials/wiki2/src/authorization/production.ini
+++ b/docs/tutorials/wiki2/src/authorization/production.ini
@@ -11,8 +11,6 @@ pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
-pyramid.includes =
- pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index 084fee19f..a62c42378 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -2,7 +2,8 @@ from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
-from security.default import groupfinder
+from .security.default import groupfinder
+
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
@@ -10,12 +11,12 @@ def main(global_config, **settings):
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
- config = Configurator(settings=settings,
- root_factory='tutorial.models.mymodel.RootFactory')
+ config = Configurator(settings=settings)
+ config.include('pyramid_jinja2')
+ config.include('.models')
+ config.set_root_factory('.models.mymodel.RootFactory')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
- config.include('pyramid_jinja2')
- config.include('.models.meta')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('login', '/login')
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py
index 7b1c62867..3d3efe06f 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py
@@ -1,7 +1,73 @@
+from sqlalchemy import engine_from_config
+from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
-# import all models classes here for sqlalchemy mappers
-# to pick up
+import zope.sqlalchemy
+
+# import or define all models here to ensure they are attached to the
+# Base.metadata prior to any initialization routines
from .mymodel import Page # flake8: noqa
-# run configure mappers to ensure we avoid any race conditions
+# run configure_mappers after defining all of the models to ensure
+# all relationships can be setup
configure_mappers()
+
+
+def get_engine(settings, prefix='sqlalchemy.'):
+ return engine_from_config(settings, prefix)
+
+
+def get_session_factory(engine):
+ factory = sessionmaker()
+ factory.configure(bind=engine)
+ return factory
+
+
+def get_tm_session(session_factory, transaction_manager):
+ """
+ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
+
+ This function will hook the session to the transaction manager which
+ will take care of committing any changes.
+
+ - When using pyramid_tm it will automatically be committed or aborted
+ depending on whether an exception is raised.
+
+ - When using scripts you should wrap the session in a manager yourself.
+ For example::
+
+ import transaction
+
+ engine = get_engine(settings)
+ session_factory = get_session_factory(engine)
+ with transaction.manager:
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ """
+ dbsession = session_factory()
+ zope.sqlalchemy.register(
+ dbsession, transaction_manager=transaction_manager)
+ return dbsession
+
+
+def includeme(config):
+ """
+ Initialize the model for a Pyramid app.
+
+ Activate this setup using ``config.include('tutorial.models')``.
+
+ """
+ settings = config.get_settings()
+
+ # use pyramid_tm to hook the transaction lifecycle to the request
+ config.include('pyramid_tm')
+
+ session_factory = get_session_factory(get_engine(settings))
+ config.registry['dbsession_factory'] = session_factory
+
+ # make request.dbsession available for use in Pyramid
+ config.add_request_method(
+ # r.tm is the transaction manager used by pyramid_tm
+ lambda r: get_tm_session(session_factory, r.tm),
+ 'dbsession',
+ reify=True
+ )
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
index 80ececd8c..fc3e8f1dd 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
@@ -1,8 +1,5 @@
-from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import MetaData
-import zope.sqlalchemy
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
@@ -17,33 +14,3 @@ NAMING_CONVENTION = {
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
-
-
-def includeme(config):
- settings = config.get_settings()
- dbmaker = get_dbmaker(get_engine(settings))
-
- config.add_request_method(
- lambda r: get_session(r.tm, dbmaker),
- 'dbsession',
- reify=True
- )
-
- config.include('pyramid_tm')
-
-
-def get_session(transaction_manager, dbmaker):
- dbsession = dbmaker()
- zope.sqlalchemy.register(dbsession,
- transaction_manager=transaction_manager)
- return dbsession
-
-
-def get_engine(settings, prefix='sqlalchemy.'):
- return engine_from_config(settings, prefix)
-
-
-def get_dbmaker(engine):
- dbmaker = sessionmaker()
- dbmaker.configure(bind=engine)
- return dbmaker
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py
index 03e2f90ca..25209c745 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py
@@ -1,15 +1,14 @@
-from .meta import Base
-
from pyramid.security import (
Allow,
Everyone,
- )
-
+)
from sqlalchemy import (
Column,
Integer,
Text,
- )
+)
+
+from .meta import Base
class Page(Base):
@@ -19,8 +18,12 @@ class Page(Base):
name = Column(Text, unique=True)
data = Column(Integer)
+
class RootFactory(object):
- __acl__ = [ (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit') ]
+ __acl__ = [
+ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit'),
+ ]
+
def __init__(self, request):
- pass \ No newline at end of file
+ pass
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
index 4aac4a848..601a6e73f 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
@@ -7,13 +7,15 @@ from pyramid.paster import (
setup_logging,
)
-from ..models.meta import (
- Base,
- get_session,
+from pyramid.scripts.common import parse_vars
+
+from ..models.meta import Base
+from ..models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
-from ..models.mymodel import Page
+from ..models import Page
def usage(argv):
@@ -27,16 +29,17 @@ def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
+ options = parse_vars(argv[2:])
setup_logging(config_uri)
- settings = get_appsettings(config_uri)
+ settings = get_appsettings(config_uri, options=options)
engine = get_engine(settings)
- dbmaker = get_dbmaker(engine)
-
- dbsession = get_session(transaction.manager, dbmaker)
-
Base.metadata.create_all(engine)
+ session_factory = get_session_factory(engine)
+
with transaction.manager:
- model = Page(name='FrontPage', data='This is the front page')
- dbsession.add(model)
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ page = Page(name='FrontPage', data='This is the front page')
+ dbsession.add(page)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py
index 5bb534f79..e69de29bb 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py
@@ -1 +0,0 @@
-# package
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py
index d88c9c71f..7fc1ea7c8 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py
@@ -1,6 +1,11 @@
-USERS = {'editor':'editor',
- 'viewer':'viewer'}
-GROUPS = {'editor':['group:editors']}
+USERS = {
+ 'editor': 'editor',
+ 'viewer': 'viewer',
+}
+
+GROUPS = {
+ 'editor': ['group:editors'],
+}
def groupfinder(userid, request):
if userid in USERS:
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2
new file mode 100644
index 000000000..1917f83c7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
+</div>
+{% endblock content %}
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
index c4f3a2c93..70ce49b73 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2
@@ -33,16 +33,16 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if logged_in %}
+ {% if request.authenticated_userid is not None %}
<p class="pull-right">
- <a href="{{ request.application_url }}/logout">Logout</a>
+ <a href="{{ request.route_url('logout') }}">Logout</a>
</p>
{% endif %}
<p>
Editing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
<form action="{{ save_url }}" method="post">
<div class="form-group">
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
index a7afc66fc..b12ca5b0c 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2
@@ -33,9 +33,9 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if logged_in %}
+ {% if request.authenticated_userid is not None %}
<p class="pull-right">
- <a href="{{ request.application_url }}/logout">Logout</a>
+ <a href="{{ request.route_url('logout') }}">Logout</a>
</p>
{% endif %}
<p>{{ content|safe }}</p>
@@ -48,7 +48,7 @@
Viewing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
index b947e3bb1..c54945c28 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py
@@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase):
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
- self.config.include('.models.meta')
+ self.config.include('.models')
settings = self.config.get_settings()
- from .models.meta import (
- get_session,
+ from .models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
self.engine = get_engine(settings)
- dbmaker = get_dbmaker(self.engine)
+ session_factory = get_session_factory(self.engine)
- self.session = get_session(transaction.manager, dbmaker)
+ self.session = get_tm_session(session_factory, transaction.manager)
def init_database(self):
- from .models.meta import Base
+ from .models import Base
Base.metadata.create_all(self.engine)
def tearDown(self):
@@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase):
testing.tearDown()
transaction.abort()
- Base.metadata.create_all(self.engine)
+ Base.metadata.drop_all(self.engine)
class TestMyViewSuccessCondition(BaseTest):
@@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest):
super(TestMyViewSuccessCondition, self).setUp()
self.init_database()
- from .models.mymodel import MyModel
+ from .models import MyModel
model = MyModel(name='one', value=55)
self.session.add(model)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
index f35f041a4..aa77facd7 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
@@ -6,31 +6,27 @@ from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
)
-
from pyramid.view import (
view_config,
forbidden_view_config,
)
-
from pyramid.security import (
remember,
forget,
)
+from ..models import Page
from ..security.default import USERS
-from ..models.mymodel import Page
-
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
-@view_config(route_name='view_wiki',
- permission='view')
+@view_config(route_name='view_wiki', permission='view')
def view_wiki(request):
- return HTTPFound(location=request.route_url('view_page',
- pagename='FrontPage'))
+ next_url = request.route_url('view_page', pagename='FrontPage')
+ return HTTPFound(location=next_url)
-@view_config(route_name='view_page', renderer='templates/view.jinja2',
+@view_config(route_name='view_page', renderer='../templates/view.jinja2',
permission='view')
def view_page(request):
pagename = request.matchdict['pagename']
@@ -51,10 +47,9 @@ def view_page(request):
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
edit_url = request.route_url('edit_page', pagename=pagename)
- return dict(page=page, content=content, edit_url=edit_url,
- logged_in=request.authenticated_userid)
+ return dict(page=page, content=content, edit_url=edit_url)
-@view_config(route_name='add_page', renderer='templates/edit.jinja2',
+@view_config(route_name='add_page', renderer='../templates/edit.jinja2',
permission='edit')
def add_page(request):
pagename = request.matchdict['pagename']
@@ -62,29 +57,27 @@ def add_page(request):
body = request.params['body']
page = Page(name=pagename, data=body)
request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
save_url = request.route_url('add_page', pagename=pagename)
page = Page(name='', data='')
- return dict(page=page, save_url=save_url,
- logged_in=request.authenticated_userid)
+ return dict(page=page, save_url=save_url)
-@view_config(route_name='edit_page', renderer='templates/edit.jinja2',
+@view_config(route_name='edit_page', renderer='../templates/edit.jinja2',
permission='edit')
def edit_page(request):
pagename = request.matchdict['pagename']
page = request.dbsession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
- request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=pagename),
- logged_in=request.authenticated_userid
+ save_url=request.route_url('edit_page', pagename=pagename),
)
+
@view_config(route_name='login', renderer='templates/login.jinja2')
@forbidden_view_config(renderer='templates/login.jinja2')
def login(request):
@@ -101,20 +94,19 @@ def login(request):
password = request.params['password']
if USERS.get(login) == password:
headers = remember(request, login)
- return HTTPFound(location = came_from,
- headers = headers)
+ return HTTPFound(location=came_from, headers=headers)
message = 'Failed login'
return dict(
- message = message,
- url = request.application_url + '/login',
- came_from = came_from,
- login = login,
- password = password,
+ message=message,
+ url=request.route_url('login'),
+ came_from=came_from,
+ login=login,
+ password=password,
)
@view_config(route_name='logout')
def logout(request):
headers = forget(request)
- return HTTPFound(location = request.route_url('view_wiki'),
- headers = headers)
+ next_url = request.route_url('view_wiki')
+ return HTTPFound(location=next_url, headers=headers)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py
new file mode 100644
index 000000000..a4b8201f1
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py
@@ -0,0 +1,5 @@
+from pyramid.view import notfound_view_config
+
+@notfound_view_config(renderer='../templates/404.jinja2')
+def notfound_view(request):
+ return {}
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py
index a4026fcd6..48a957ecb 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py
@@ -62,6 +62,7 @@ def includeme(config):
config.include('pyramid_tm')
session_factory = get_session_factory(get_engine(settings))
+ config.registry['dbsession_factory'] = session_factory
# make request.dbsession available for use in Pyramid
config.add_request_method(
diff --git a/docs/tutorials/wiki2/src/models/MANIFEST.in b/docs/tutorials/wiki2/src/models/MANIFEST.in
index 81beba1b1..42cd299b5 100644
--- a/docs/tutorials/wiki2/src/models/MANIFEST.in
+++ b/docs/tutorials/wiki2/src/models/MANIFEST.in
@@ -1,2 +1,2 @@
include *.txt *.ini *.cfg *.rst
-recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
+recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini
index 97acfbd7d..cb1db3211 100644
--- a/docs/tutorials/wiki2/src/models/production.ini
+++ b/docs/tutorials/wiki2/src/models/production.ini
@@ -11,8 +11,6 @@ pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
-pyramid.includes =
- pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
index 7994bbfa8..17763812a 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -6,7 +6,7 @@ def main(global_config, **settings):
"""
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
- config.include('.models.meta')
+ config.include('.models')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py
index 7b1c62867..3d3efe06f 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py
@@ -1,7 +1,73 @@
+from sqlalchemy import engine_from_config
+from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
-# import all models classes here for sqlalchemy mappers
-# to pick up
+import zope.sqlalchemy
+
+# import or define all models here to ensure they are attached to the
+# Base.metadata prior to any initialization routines
from .mymodel import Page # flake8: noqa
-# run configure mappers to ensure we avoid any race conditions
+# run configure_mappers after defining all of the models to ensure
+# all relationships can be setup
configure_mappers()
+
+
+def get_engine(settings, prefix='sqlalchemy.'):
+ return engine_from_config(settings, prefix)
+
+
+def get_session_factory(engine):
+ factory = sessionmaker()
+ factory.configure(bind=engine)
+ return factory
+
+
+def get_tm_session(session_factory, transaction_manager):
+ """
+ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
+
+ This function will hook the session to the transaction manager which
+ will take care of committing any changes.
+
+ - When using pyramid_tm it will automatically be committed or aborted
+ depending on whether an exception is raised.
+
+ - When using scripts you should wrap the session in a manager yourself.
+ For example::
+
+ import transaction
+
+ engine = get_engine(settings)
+ session_factory = get_session_factory(engine)
+ with transaction.manager:
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ """
+ dbsession = session_factory()
+ zope.sqlalchemy.register(
+ dbsession, transaction_manager=transaction_manager)
+ return dbsession
+
+
+def includeme(config):
+ """
+ Initialize the model for a Pyramid app.
+
+ Activate this setup using ``config.include('tutorial.models')``.
+
+ """
+ settings = config.get_settings()
+
+ # use pyramid_tm to hook the transaction lifecycle to the request
+ config.include('pyramid_tm')
+
+ session_factory = get_session_factory(get_engine(settings))
+ config.registry['dbsession_factory'] = session_factory
+
+ # make request.dbsession available for use in Pyramid
+ config.add_request_method(
+ # r.tm is the transaction manager used by pyramid_tm
+ lambda r: get_tm_session(session_factory, r.tm),
+ 'dbsession',
+ reify=True
+ )
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py
index 80ececd8c..fc3e8f1dd 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py
@@ -1,8 +1,5 @@
-from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import MetaData
-import zope.sqlalchemy
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
@@ -17,33 +14,3 @@ NAMING_CONVENTION = {
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
-
-
-def includeme(config):
- settings = config.get_settings()
- dbmaker = get_dbmaker(get_engine(settings))
-
- config.add_request_method(
- lambda r: get_session(r.tm, dbmaker),
- 'dbsession',
- reify=True
- )
-
- config.include('pyramid_tm')
-
-
-def get_session(transaction_manager, dbmaker):
- dbsession = dbmaker()
- zope.sqlalchemy.register(dbsession,
- transaction_manager=transaction_manager)
- return dbsession
-
-
-def get_engine(settings, prefix='sqlalchemy.'):
- return engine_from_config(settings, prefix)
-
-
-def get_dbmaker(engine):
- dbmaker = sessionmaker()
- dbmaker.configure(bind=engine)
- return dbmaker
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py
index 45571d78e..b23d0c0d2 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py
@@ -1,10 +1,11 @@
-from .meta import Base
from sqlalchemy import (
Column,
Integer,
Text,
)
+from .meta import Base
+
class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
index 4aac4a848..601a6e73f 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
@@ -7,13 +7,15 @@ from pyramid.paster import (
setup_logging,
)
-from ..models.meta import (
- Base,
- get_session,
+from pyramid.scripts.common import parse_vars
+
+from ..models.meta import Base
+from ..models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
-from ..models.mymodel import Page
+from ..models import Page
def usage(argv):
@@ -27,16 +29,17 @@ def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
+ options = parse_vars(argv[2:])
setup_logging(config_uri)
- settings = get_appsettings(config_uri)
+ settings = get_appsettings(config_uri, options=options)
engine = get_engine(settings)
- dbmaker = get_dbmaker(engine)
-
- dbsession = get_session(transaction.manager, dbmaker)
-
Base.metadata.create_all(engine)
+ session_factory = get_session_factory(engine)
+
with transaction.manager:
- model = Page(name='FrontPage', data='This is the front page')
- dbsession.add(model)
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ page = Page(name='FrontPage', data='This is the front page')
+ dbsession.add(page)
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2
new file mode 100644
index 000000000..1917f83c7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
+</div>
+{% endblock content %}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/tests.py b/docs/tutorials/wiki2/src/models/tutorial/tests.py
index b947e3bb1..c54945c28 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/tests.py
@@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase):
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
- self.config.include('.models.meta')
+ self.config.include('.models')
settings = self.config.get_settings()
- from .models.meta import (
- get_session,
+ from .models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
self.engine = get_engine(settings)
- dbmaker = get_dbmaker(self.engine)
+ session_factory = get_session_factory(self.engine)
- self.session = get_session(transaction.manager, dbmaker)
+ self.session = get_tm_session(session_factory, transaction.manager)
def init_database(self):
- from .models.meta import Base
+ from .models import Base
Base.metadata.create_all(self.engine)
def tearDown(self):
@@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase):
testing.tearDown()
transaction.abort()
- Base.metadata.create_all(self.engine)
+ Base.metadata.drop_all(self.engine)
class TestMyViewSuccessCondition(BaseTest):
@@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest):
super(TestMyViewSuccessCondition, self).setUp()
self.init_database()
- from .models.mymodel import MyModel
+ from .models import MyModel
model = MyModel(name='one', value=55)
self.session.add(model)
diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/default.py b/docs/tutorials/wiki2/src/models/tutorial/views/default.py
index 13ad8793c..ad0c728d7 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/views/default.py
@@ -3,7 +3,7 @@ from pyramid.view import view_config
from sqlalchemy.exc import DBAPIError
-from ..models.mymodel import MyModel
+from ..models import MyModel
@view_config(route_name='home', renderer='../templates/mytemplate.jinja2')
@@ -12,7 +12,7 @@ def my_view(request):
query = request.dbsession.query(MyModel)
one = query.filter(MyModel.name == 'one').first()
except DBAPIError:
- return Response(db_err_msg, content_type='text/plain', status_int=500)
+ return Response(db_err_msg, content_type='text/plain', status=500)
return {'one': one, 'project': 'tutorial'}
diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/errors.py b/docs/tutorials/wiki2/src/models/tutorial/views/errors.py
new file mode 100644
index 000000000..a4b8201f1
--- /dev/null
+++ b/docs/tutorials/wiki2/src/models/tutorial/views/errors.py
@@ -0,0 +1,5 @@
+from pyramid.view import notfound_view_config
+
+@notfound_view_config(renderer='../templates/404.jinja2')
+def notfound_view(request):
+ return {}
diff --git a/docs/tutorials/wiki2/src/tests/MANIFEST.in b/docs/tutorials/wiki2/src/tests/MANIFEST.in
index 81beba1b1..42cd299b5 100644
--- a/docs/tutorials/wiki2/src/tests/MANIFEST.in
+++ b/docs/tutorials/wiki2/src/tests/MANIFEST.in
@@ -1,2 +1,2 @@
include *.txt *.ini *.cfg *.rst
-recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
+recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini
index 97acfbd7d..cb1db3211 100644
--- a/docs/tutorials/wiki2/src/tests/production.ini
+++ b/docs/tutorials/wiki2/src/tests/production.ini
@@ -11,8 +11,6 @@ pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
-pyramid.includes =
- pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py
index f640b4399..d4e5a4072 100644
--- a/docs/tutorials/wiki2/src/tests/setup.py
+++ b/docs/tutorials/wiki2/src/tests/setup.py
@@ -18,7 +18,6 @@ requires = [
'zope.sqlalchemy',
'waitress',
'docutils',
- 'WebTest',
]
setup(name='tutorial',
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
index 084fee19f..a62c42378 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
@@ -2,7 +2,8 @@ from pyramid.config import Configurator
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
-from security.default import groupfinder
+from .security.default import groupfinder
+
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
@@ -10,12 +11,12 @@ def main(global_config, **settings):
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
- config = Configurator(settings=settings,
- root_factory='tutorial.models.mymodel.RootFactory')
+ config = Configurator(settings=settings)
+ config.include('pyramid_jinja2')
+ config.include('.models')
+ config.set_root_factory('.models.mymodel.RootFactory')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
- config.include('pyramid_jinja2')
- config.include('.models.meta')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('login', '/login')
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py
index 7b1c62867..3d3efe06f 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py
@@ -1,7 +1,73 @@
+from sqlalchemy import engine_from_config
+from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
-# import all models classes here for sqlalchemy mappers
-# to pick up
+import zope.sqlalchemy
+
+# import or define all models here to ensure they are attached to the
+# Base.metadata prior to any initialization routines
from .mymodel import Page # flake8: noqa
-# run configure mappers to ensure we avoid any race conditions
+# run configure_mappers after defining all of the models to ensure
+# all relationships can be setup
configure_mappers()
+
+
+def get_engine(settings, prefix='sqlalchemy.'):
+ return engine_from_config(settings, prefix)
+
+
+def get_session_factory(engine):
+ factory = sessionmaker()
+ factory.configure(bind=engine)
+ return factory
+
+
+def get_tm_session(session_factory, transaction_manager):
+ """
+ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
+
+ This function will hook the session to the transaction manager which
+ will take care of committing any changes.
+
+ - When using pyramid_tm it will automatically be committed or aborted
+ depending on whether an exception is raised.
+
+ - When using scripts you should wrap the session in a manager yourself.
+ For example::
+
+ import transaction
+
+ engine = get_engine(settings)
+ session_factory = get_session_factory(engine)
+ with transaction.manager:
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ """
+ dbsession = session_factory()
+ zope.sqlalchemy.register(
+ dbsession, transaction_manager=transaction_manager)
+ return dbsession
+
+
+def includeme(config):
+ """
+ Initialize the model for a Pyramid app.
+
+ Activate this setup using ``config.include('tutorial.models')``.
+
+ """
+ settings = config.get_settings()
+
+ # use pyramid_tm to hook the transaction lifecycle to the request
+ config.include('pyramid_tm')
+
+ session_factory = get_session_factory(get_engine(settings))
+ config.registry['dbsession_factory'] = session_factory
+
+ # make request.dbsession available for use in Pyramid
+ config.add_request_method(
+ # r.tm is the transaction manager used by pyramid_tm
+ lambda r: get_tm_session(session_factory, r.tm),
+ 'dbsession',
+ reify=True
+ )
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py
index 80ececd8c..fc3e8f1dd 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py
@@ -1,8 +1,5 @@
-from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import MetaData
-import zope.sqlalchemy
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
@@ -17,33 +14,3 @@ NAMING_CONVENTION = {
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
-
-
-def includeme(config):
- settings = config.get_settings()
- dbmaker = get_dbmaker(get_engine(settings))
-
- config.add_request_method(
- lambda r: get_session(r.tm, dbmaker),
- 'dbsession',
- reify=True
- )
-
- config.include('pyramid_tm')
-
-
-def get_session(transaction_manager, dbmaker):
- dbsession = dbmaker()
- zope.sqlalchemy.register(dbsession,
- transaction_manager=transaction_manager)
- return dbsession
-
-
-def get_engine(settings, prefix='sqlalchemy.'):
- return engine_from_config(settings, prefix)
-
-
-def get_dbmaker(engine):
- dbmaker = sessionmaker()
- dbmaker.configure(bind=engine)
- return dbmaker
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py
index 03e2f90ca..25209c745 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py
@@ -1,15 +1,14 @@
-from .meta import Base
-
from pyramid.security import (
Allow,
Everyone,
- )
-
+)
from sqlalchemy import (
Column,
Integer,
Text,
- )
+)
+
+from .meta import Base
class Page(Base):
@@ -19,8 +18,12 @@ class Page(Base):
name = Column(Text, unique=True)
data = Column(Integer)
+
class RootFactory(object):
- __acl__ = [ (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit') ]
+ __acl__ = [
+ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit'),
+ ]
+
def __init__(self, request):
- pass \ No newline at end of file
+ pass
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
index 4aac4a848..601a6e73f 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
@@ -7,13 +7,15 @@ from pyramid.paster import (
setup_logging,
)
-from ..models.meta import (
- Base,
- get_session,
+from pyramid.scripts.common import parse_vars
+
+from ..models.meta import Base
+from ..models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
-from ..models.mymodel import Page
+from ..models import Page
def usage(argv):
@@ -27,16 +29,17 @@ def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
+ options = parse_vars(argv[2:])
setup_logging(config_uri)
- settings = get_appsettings(config_uri)
+ settings = get_appsettings(config_uri, options=options)
engine = get_engine(settings)
- dbmaker = get_dbmaker(engine)
-
- dbsession = get_session(transaction.manager, dbmaker)
-
Base.metadata.create_all(engine)
+ session_factory = get_session_factory(engine)
+
with transaction.manager:
- model = Page(name='FrontPage', data='This is the front page')
- dbsession.add(model)
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ page = Page(name='FrontPage', data='This is the front page')
+ dbsession.add(page)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py
index 5bb534f79..e69de29bb 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py
@@ -1 +0,0 @@
-# package
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py
index d88c9c71f..7fc1ea7c8 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py
@@ -1,6 +1,11 @@
-USERS = {'editor':'editor',
- 'viewer':'viewer'}
-GROUPS = {'editor':['group:editors']}
+USERS = {
+ 'editor': 'editor',
+ 'viewer': 'viewer',
+}
+
+GROUPS = {
+ 'editor': ['group:editors'],
+}
def groupfinder(userid, request):
if userid in USERS:
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2
new file mode 100644
index 000000000..1917f83c7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
+</div>
+{% endblock content %}
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2
index c4f3a2c93..70ce49b73 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2
@@ -33,16 +33,16 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if logged_in %}
+ {% if request.authenticated_userid is not None %}
<p class="pull-right">
- <a href="{{ request.application_url }}/logout">Logout</a>
+ <a href="{{ request.route_url('logout') }}">Logout</a>
</p>
{% endif %}
<p>
Editing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
<form action="{{ save_url }}" method="post">
<div class="form-group">
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2
index a7afc66fc..b12ca5b0c 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2
@@ -33,9 +33,9 @@
</div>
<div class="col-md-10">
<div class="content">
- {% if logged_in %}
+ {% if request.authenticated_userid is not None %}
<p class="pull-right">
- <a href="{{ request.application_url }}/logout">Logout</a>
+ <a href="{{ request.route_url('logout') }}">Logout</a>
</p>
{% endif %}
<p>{{ content|safe }}</p>
@@ -48,7 +48,7 @@
Viewing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py
index 8b1378917..e69de29bb 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py
@@ -1 +0,0 @@
-
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
index 339c60bc2..eda47c064 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py
@@ -1,17 +1,5 @@
import unittest
-from pyramid import testing
-
-
-def dummy_request(dbsession):
- return testing.DummyRequest(dbsession=dbsession)
-
-
-def _register_routes(config):
- config.add_route('view_page', '{pagename}')
- config.add_route('edit_page', '{pagename}/edit_page')
- config.add_route('add_page', 'add_page/{pagename}')
-
class FunctionalTests(unittest.TestCase):
@@ -27,11 +15,8 @@ class FunctionalTests(unittest.TestCase):
@staticmethod
def setup_database():
import transaction
- from tutorial.models.mymodel import Page
- from tutorial.models.meta import (
- Base,
- )
- import tutorial.models.meta
+ from tutorial.models import Page
+ from tutorial.models.meta import Base
def initialize_db(dbsession, engine):
@@ -40,11 +25,9 @@ class FunctionalTests(unittest.TestCase):
model = Page(name='FrontPage', data='This is the front page')
dbsession.add(model)
- def wrap_get_session(transaction_manager, dbmaker):
- dbsession = get_session(transaction_manager, dbmaker)
+ def wrap_get_tm_session(session_factory, transaction_manager):
+ dbsession = get_tm_session(session_factory, transaction_manager)
initialize_db(dbsession, engine)
- tutorial.models.meta.get_session = get_session
- tutorial.models.meta.get_engine = get_engine
return dbsession
def wrap_get_engine(settings):
@@ -53,10 +36,10 @@ class FunctionalTests(unittest.TestCase):
return engine
get_session = tutorial.models.meta.get_session
- tutorial.models.meta.get_session = wrap_get_session
+ tutorial.models.get_tm_session = wrap_get_tm_session
get_engine = tutorial.models.meta.get_engine
- tutorial.models.meta.get_engine = wrap_get_engine
+ tutorial.models.get_engine = wrap_get_engine
@classmethod
def setUpClass(cls):
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py
index d70311e38..81d84fa30 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py
@@ -10,35 +10,29 @@ def dummy_request(dbsession):
def _register_routes(config):
config.add_route('view_page', '{pagename}')
- config.add_route('edit_page', '{pagename}/edit_page')
config.add_route('add_page', 'add_page/{pagename}')
+ config.add_route('edit_page', '{pagename}/edit_page')
class BaseTest(unittest.TestCase):
def setUp(self):
+ from ..models import get_tm_session
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
- self.config.include('..models.meta')
- _register_routes(self.config)
- settings = self.config.get_settings()
+ self.config.include('..models')
+ self.config.include(_register_routes)
- from ..models.meta import (
- get_session,
- get_engine,
- get_dbmaker,
- )
-
- self.engine = get_engine(settings)
- dbmaker = get_dbmaker(self.engine)
-
- self.session = get_session(transaction.manager, dbmaker)
+ session_factory = self.config.registry['dbsession_factory']
+ self.session = get_tm_session(session_factory, transaction.manager)
self.init_database()
def init_database(self):
from ..models.meta import Base
- Base.metadata.create_all(self.engine)
+ session_factory = self.config.registry['dbsession_factory']
+ engine = session_factory.get_bind()
+ Base.metadata.create_all(engine)
def tearDown(self):
testing.tearDown()
@@ -46,7 +40,6 @@ class BaseTest(unittest.TestCase):
class ViewWikiTests(unittest.TestCase):
-
def setUp(self):
self.config = testing.setUp()
_register_routes(self.config)
@@ -65,13 +58,6 @@ class ViewWikiTests(unittest.TestCase):
class ViewPageTests(BaseTest):
- def setUp(self):
- super(ViewPageTests, self).setUp()
-
- def tearDown(self):
- transaction.abort()
- testing.tearDown()
-
def _callFUT(self, request):
from tutorial.views.default import view_page
return view_page(request)
@@ -102,13 +88,6 @@ class ViewPageTests(BaseTest):
class AddPageTests(BaseTest):
- def setUp(self):
- super(AddPageTests, self).setUp()
-
- def tearDown(self):
- transaction.abort()
- testing.tearDown()
-
def _callFUT(self, request):
from tutorial.views.default import add_page
return add_page(request)
@@ -133,13 +112,6 @@ class AddPageTests(BaseTest):
class EditPageTests(BaseTest):
- def setUp(self):
- super(EditPageTests, self).setUp()
-
- def tearDown(self):
- transaction.abort()
- testing.tearDown()
-
def _callFUT(self, request):
from tutorial.views.default import edit_page
return edit_page(request)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
index f35f041a4..aa77facd7 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py
@@ -6,31 +6,27 @@ from pyramid.httpexceptions import (
HTTPFound,
HTTPNotFound,
)
-
from pyramid.view import (
view_config,
forbidden_view_config,
)
-
from pyramid.security import (
remember,
forget,
)
+from ..models import Page
from ..security.default import USERS
-from ..models.mymodel import Page
-
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
-@view_config(route_name='view_wiki',
- permission='view')
+@view_config(route_name='view_wiki', permission='view')
def view_wiki(request):
- return HTTPFound(location=request.route_url('view_page',
- pagename='FrontPage'))
+ next_url = request.route_url('view_page', pagename='FrontPage')
+ return HTTPFound(location=next_url)
-@view_config(route_name='view_page', renderer='templates/view.jinja2',
+@view_config(route_name='view_page', renderer='../templates/view.jinja2',
permission='view')
def view_page(request):
pagename = request.matchdict['pagename']
@@ -51,10 +47,9 @@ def view_page(request):
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
edit_url = request.route_url('edit_page', pagename=pagename)
- return dict(page=page, content=content, edit_url=edit_url,
- logged_in=request.authenticated_userid)
+ return dict(page=page, content=content, edit_url=edit_url)
-@view_config(route_name='add_page', renderer='templates/edit.jinja2',
+@view_config(route_name='add_page', renderer='../templates/edit.jinja2',
permission='edit')
def add_page(request):
pagename = request.matchdict['pagename']
@@ -62,29 +57,27 @@ def add_page(request):
body = request.params['body']
page = Page(name=pagename, data=body)
request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
save_url = request.route_url('add_page', pagename=pagename)
page = Page(name='', data='')
- return dict(page=page, save_url=save_url,
- logged_in=request.authenticated_userid)
+ return dict(page=page, save_url=save_url)
-@view_config(route_name='edit_page', renderer='templates/edit.jinja2',
+@view_config(route_name='edit_page', renderer='../templates/edit.jinja2',
permission='edit')
def edit_page(request):
pagename = request.matchdict['pagename']
page = request.dbsession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
- request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=pagename),
- logged_in=request.authenticated_userid
+ save_url=request.route_url('edit_page', pagename=pagename),
)
+
@view_config(route_name='login', renderer='templates/login.jinja2')
@forbidden_view_config(renderer='templates/login.jinja2')
def login(request):
@@ -101,20 +94,19 @@ def login(request):
password = request.params['password']
if USERS.get(login) == password:
headers = remember(request, login)
- return HTTPFound(location = came_from,
- headers = headers)
+ return HTTPFound(location=came_from, headers=headers)
message = 'Failed login'
return dict(
- message = message,
- url = request.application_url + '/login',
- came_from = came_from,
- login = login,
- password = password,
+ message=message,
+ url=request.route_url('login'),
+ came_from=came_from,
+ login=login,
+ password=password,
)
@view_config(route_name='logout')
def logout(request):
headers = forget(request)
- return HTTPFound(location = request.route_url('view_wiki'),
- headers = headers)
+ next_url = request.route_url('view_wiki')
+ return HTTPFound(location=next_url, headers=headers)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py
new file mode 100644
index 000000000..a4b8201f1
--- /dev/null
+++ b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py
@@ -0,0 +1,5 @@
+from pyramid.view import notfound_view_config
+
+@notfound_view_config(renderer='../templates/404.jinja2')
+def notfound_view(request):
+ return {}
diff --git a/docs/tutorials/wiki2/src/views/MANIFEST.in b/docs/tutorials/wiki2/src/views/MANIFEST.in
index 81beba1b1..42cd299b5 100644
--- a/docs/tutorials/wiki2/src/views/MANIFEST.in
+++ b/docs/tutorials/wiki2/src/views/MANIFEST.in
@@ -1,2 +1,2 @@
include *.txt *.ini *.cfg *.rst
-recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
+recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini
index 97acfbd7d..cb1db3211 100644
--- a/docs/tutorials/wiki2/src/views/production.ini
+++ b/docs/tutorials/wiki2/src/views/production.ini
@@ -11,8 +11,6 @@ pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
-pyramid.includes =
- pyramid_tm
sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index d28f09ca4..5d8c7fba2 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -6,7 +6,7 @@ def main(global_config, **settings):
"""
config = Configurator(settings=settings)
config.include('pyramid_jinja2')
- config.include('.models.meta')
+ config.include('.models')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('view_page', '/{pagename}')
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py
index 7b1c62867..3d3efe06f 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py
@@ -1,7 +1,73 @@
+from sqlalchemy import engine_from_config
+from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import configure_mappers
-# import all models classes here for sqlalchemy mappers
-# to pick up
+import zope.sqlalchemy
+
+# import or define all models here to ensure they are attached to the
+# Base.metadata prior to any initialization routines
from .mymodel import Page # flake8: noqa
-# run configure mappers to ensure we avoid any race conditions
+# run configure_mappers after defining all of the models to ensure
+# all relationships can be setup
configure_mappers()
+
+
+def get_engine(settings, prefix='sqlalchemy.'):
+ return engine_from_config(settings, prefix)
+
+
+def get_session_factory(engine):
+ factory = sessionmaker()
+ factory.configure(bind=engine)
+ return factory
+
+
+def get_tm_session(session_factory, transaction_manager):
+ """
+ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
+
+ This function will hook the session to the transaction manager which
+ will take care of committing any changes.
+
+ - When using pyramid_tm it will automatically be committed or aborted
+ depending on whether an exception is raised.
+
+ - When using scripts you should wrap the session in a manager yourself.
+ For example::
+
+ import transaction
+
+ engine = get_engine(settings)
+ session_factory = get_session_factory(engine)
+ with transaction.manager:
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ """
+ dbsession = session_factory()
+ zope.sqlalchemy.register(
+ dbsession, transaction_manager=transaction_manager)
+ return dbsession
+
+
+def includeme(config):
+ """
+ Initialize the model for a Pyramid app.
+
+ Activate this setup using ``config.include('tutorial.models')``.
+
+ """
+ settings = config.get_settings()
+
+ # use pyramid_tm to hook the transaction lifecycle to the request
+ config.include('pyramid_tm')
+
+ session_factory = get_session_factory(get_engine(settings))
+ config.registry['dbsession_factory'] = session_factory
+
+ # make request.dbsession available for use in Pyramid
+ config.add_request_method(
+ # r.tm is the transaction manager used by pyramid_tm
+ lambda r: get_tm_session(session_factory, r.tm),
+ 'dbsession',
+ reify=True
+ )
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py
index 80ececd8c..fc3e8f1dd 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py
@@ -1,8 +1,5 @@
-from sqlalchemy import engine_from_config
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
from sqlalchemy.schema import MetaData
-import zope.sqlalchemy
# Recommended naming convention used by Alembic, as various different database
# providers will autogenerate vastly different names making migrations more
@@ -17,33 +14,3 @@ NAMING_CONVENTION = {
metadata = MetaData(naming_convention=NAMING_CONVENTION)
Base = declarative_base(metadata=metadata)
-
-
-def includeme(config):
- settings = config.get_settings()
- dbmaker = get_dbmaker(get_engine(settings))
-
- config.add_request_method(
- lambda r: get_session(r.tm, dbmaker),
- 'dbsession',
- reify=True
- )
-
- config.include('pyramid_tm')
-
-
-def get_session(transaction_manager, dbmaker):
- dbsession = dbmaker()
- zope.sqlalchemy.register(dbsession,
- transaction_manager=transaction_manager)
- return dbsession
-
-
-def get_engine(settings, prefix='sqlalchemy.'):
- return engine_from_config(settings, prefix)
-
-
-def get_dbmaker(engine):
- dbmaker = sessionmaker()
- dbmaker.configure(bind=engine)
- return dbmaker
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py
index 45571d78e..b23d0c0d2 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py
@@ -1,10 +1,11 @@
-from .meta import Base
from sqlalchemy import (
Column,
Integer,
Text,
)
+from .meta import Base
+
class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
index 4aac4a848..601a6e73f 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
@@ -7,13 +7,15 @@ from pyramid.paster import (
setup_logging,
)
-from ..models.meta import (
- Base,
- get_session,
+from pyramid.scripts.common import parse_vars
+
+from ..models.meta import Base
+from ..models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
-from ..models.mymodel import Page
+from ..models import Page
def usage(argv):
@@ -27,16 +29,17 @@ def main(argv=sys.argv):
if len(argv) < 2:
usage(argv)
config_uri = argv[1]
+ options = parse_vars(argv[2:])
setup_logging(config_uri)
- settings = get_appsettings(config_uri)
+ settings = get_appsettings(config_uri, options=options)
engine = get_engine(settings)
- dbmaker = get_dbmaker(engine)
-
- dbsession = get_session(transaction.manager, dbmaker)
-
Base.metadata.create_all(engine)
+ session_factory = get_session_factory(engine)
+
with transaction.manager:
- model = Page(name='FrontPage', data='This is the front page')
- dbsession.add(model)
+ dbsession = get_tm_session(session_factory, transaction.manager)
+
+ page = Page(name='FrontPage', data='This is the front page')
+ dbsession.add(page)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2
new file mode 100644
index 000000000..1917f83c7
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2
@@ -0,0 +1,8 @@
+{% extends "layout.jinja2" %}
+
+{% block content %}
+<div class="content">
+ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy scaffold</span></h1>
+ <p class="lead"><span class="font-semi-bold">404</span> Page Not Found</p>
+</div>
+{% endblock content %}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
index b3aadfc2e..a41d232e5 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2
@@ -37,7 +37,7 @@
Editing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
<form action="{{ save_url }}" method="post">
<div class="form-group">
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
index 36bb96870..fa09baf70 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2
@@ -43,7 +43,7 @@
Viewing <strong>{% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}</strong>
</p>
<p>You can return to the
- <a href="{{request.application_url}}">FrontPage</a>.
+ <a href="{{request.route_url('view_page', pagename='FrontPage')}}">FrontPage</a>.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py
index b947e3bb1..c54945c28 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/tests.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py
@@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase):
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
- self.config.include('.models.meta')
+ self.config.include('.models')
settings = self.config.get_settings()
- from .models.meta import (
- get_session,
+ from .models import (
get_engine,
- get_dbmaker,
+ get_session_factory,
+ get_tm_session,
)
self.engine = get_engine(settings)
- dbmaker = get_dbmaker(self.engine)
+ session_factory = get_session_factory(self.engine)
- self.session = get_session(transaction.manager, dbmaker)
+ self.session = get_tm_session(session_factory, transaction.manager)
def init_database(self):
- from .models.meta import Base
+ from .models import Base
Base.metadata.create_all(self.engine)
def tearDown(self):
@@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase):
testing.tearDown()
transaction.abort()
- Base.metadata.create_all(self.engine)
+ Base.metadata.drop_all(self.engine)
class TestMyViewSuccessCondition(BaseTest):
@@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest):
super(TestMyViewSuccessCondition, self).setUp()
self.init_database()
- from .models.mymodel import MyModel
+ from .models import MyModel
model = MyModel(name='one', value=55)
self.session.add(model)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
index 3e5c61a72..96df85a97 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py
@@ -9,17 +9,17 @@ from pyramid.httpexceptions import (
from pyramid.view import view_config
-from ..models.mymodel import Page
+from ..models import Page
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
@view_config(route_name='view_wiki')
def view_wiki(request):
- return HTTPFound(location=request.route_url('view_page',
- pagename='FrontPage'))
+ next_url = request.route_url('view_page', pagename='FrontPage')
+ return HTTPFound(location=next_url)
-@view_config(route_name='view_page', renderer='templates/view.jinja2')
+@view_config(route_name='view_page', renderer='../templates/view.jinja2')
def view_page(request):
pagename = request.matchdict['pagename']
page = request.dbsession.query(Page).filter_by(name=pagename).first()
@@ -41,29 +41,28 @@ def view_page(request):
edit_url = request.route_url('edit_page', pagename=pagename)
return dict(page=page, content=content, edit_url=edit_url)
-@view_config(route_name='add_page', renderer='templates/edit.jinja2')
+@view_config(route_name='add_page', renderer='../templates/edit.jinja2')
def add_page(request):
pagename = request.matchdict['pagename']
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(name=pagename, data=body)
request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
save_url = request.route_url('add_page', pagename=pagename)
page = Page(name='', data='')
return dict(page=page, save_url=save_url)
-@view_config(route_name='edit_page', renderer='templates/edit.jinja2')
+@view_config(route_name='edit_page', renderer='../templates/edit.jinja2')
def edit_page(request):
pagename = request.matchdict['pagename']
page = request.dbsession.query(Page).filter_by(name=pagename).one()
if 'form.submitted' in request.params:
page.data = request.params['body']
- request.dbsession.add(page)
- return HTTPFound(location = request.route_url('view_page',
- pagename=pagename))
+ next_url = request.route_url('view_page', pagename=pagename)
+ return HTTPFound(location=next_url)
return dict(
page=page,
- save_url = request.route_url('edit_page', pagename=pagename),
+ save_url=request.route_url('edit_page', pagename=pagename),
)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/errors.py b/docs/tutorials/wiki2/src/views/tutorial/views/errors.py
new file mode 100644
index 000000000..a4b8201f1
--- /dev/null
+++ b/docs/tutorials/wiki2/src/views/tutorial/views/errors.py
@@ -0,0 +1,5 @@
+from pyramid.view import notfound_view_config
+
+@notfound_view_config(renderer='../templates/404.jinja2')
+def notfound_view(request):
+ return {}
diff --git a/docs/tutorials/wiki2/tests.rst b/docs/tutorials/wiki2/tests.rst
index fe3fdaf2c..a99cd68cc 100644
--- a/docs/tutorials/wiki2/tests.rst
+++ b/docs/tutorials/wiki2/tests.rst
@@ -18,6 +18,14 @@ subpackage, and add several new tests.
Start by creating a new directory and a new empty file ``tests/__init__.py``.
+.. warning::
+
+ It is very important when refactoring a Python module into a package to
+ be sure to delete the cache files (``.pyc`` files or ``__pycache__``
+ folders) sitting around! Python will prioritize the cache files before
+ traversing into folders and so it will use the old code and you will wonder
+ why none of your changes are working!
+
Test the views
==============