Merge pull request #2505 from stevepiercy/docs/quick-tour-gotcher-nose
Docs/quick tour gotcher nose
1 files deleted
27 files modified
| | |
| | | 'jinja2': ('http://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/', None), |
| | | 'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None), |
| | | 'python': ('https://docs.python.org/3', None), |
| | | 'pytest': ('http://pytest.org/latest/', None), |
| | | 'sqla': ('http://docs.sqlalchemy.org/en/latest', None), |
| | | 'tm': ('http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/', None), |
| | | 'toolbar': ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest', None), |
| | |
| | | ``f``, ``false``, ``n``, ``no``, ``off`` and ``0``. |
| | | |
| | | pip |
| | | The `Python Packaging Authority's <https://www.pypa.io/>`_ recommended |
| | | tool for installing Python packages. |
| | | The :term:`Python Packaging Authority`'s recommended tool for installing |
| | | Python packages. |
| | | |
| | | pyvenv |
| | | The Python Packaging Authority formerly recommended using this command |
| | | for `creating virtual environments on Python 3.4 and 3.5 |
| | | The :term:`Python Packaging Authority` formerly recommended using the |
| | | ``pyvenv`` command for `creating virtual environments on Python 3.4 and |
| | | 3.5 |
| | | <https://packaging.python.org/en/latest/installing/#creating-virtual-environments>`_, |
| | | but it is deprecated in 3.6 in favor of ``python3 -m venv`` on UNIX or |
| | | but it was deprecated in 3.6 in favor of ``python3 -m venv`` on UNIX or |
| | | ``python -m venv`` on Windows, which is backward compatible on Python |
| | | 3.3 and greater. |
| | | |
| | |
| | | use by a particular application, rather than being installed system wide. |
| | | |
| | | venv |
| | | The `Python Packaging Authority's <https://www.pypa.io/>`_ recommended |
| | | tool for creating virtual environments on Python 3.3 and greater. |
| | | The :term:`Python Packaging Authority`'s recommended tool for creating |
| | | virtual environments on Python 3.3 and greater. |
| | | |
| | | Note: whenever you encounter commands prefixed with ``$VENV`` (Unix) |
| | | or ``%VENV`` (Windows), know that that is the environment variable whose |
| | | value is the root of the virtual environment in question. |
| | | |
| | | Python Packaging Authority |
| | | The `Python Packaging Authority (PyPA) <https://www.pypa.io/en/latest/>`_ |
| | | is a working group that maintains many of the relevant projects in Python |
| | | packaging. |
| | |
| | | 20: Logins With Authentication |
| | | ============================== |
| | | |
| | | Login views that authenticate a username/password against a list of |
| | | users. |
| | | Login views that authenticate a username and password against a list of users. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Most web applications have URLs that allow people to add/edit/delete |
| | | content via a web browser. Time to add |
| | | :ref:`security <security_chapter>` |
| | | to the application. In this first step we introduce authentication. |
| | | That is, logging in and logging out using Pyramid's rich facilities for |
| | | pluggable user storages. |
| | | Most web applications have URLs that allow people to add/edit/delete content |
| | | via a web browser. Time to add :ref:`security <security_chapter>` to the |
| | | application. In this first step we introduce authentication. That is, logging |
| | | in and logging out, using Pyramid's rich facilities for pluggable user storage. |
| | | |
| | | In the next step we will introduce protection resources with |
| | | authorization security statements. |
| | | In the next step we will introduce protection of resources with authorization |
| | | security statements. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Introduce the Pyramid concepts of authentication |
| | | - Introduce the Pyramid concepts of authentication. |
| | | |
| | | - Create login/logout views |
| | | - Create login and logout views. |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Put the security hash in the ``authentication/development.ini`` |
| | | configuration file as ``tutorial.secret`` instead of putting it in |
| | | the code: |
| | | configuration file as ``tutorial.secret`` instead of putting it in the code: |
| | | |
| | | .. literalinclude:: authentication/development.ini |
| | | :language: ini |
| | | :linenos: |
| | | |
| | | #. Get authentication (and for now, authorization policies) and login |
| | | route into the :term:`configurator` in |
| | | ``authentication/tutorial/__init__.py``: |
| | | #. Get authentication (and for now, authorization policies) and login route |
| | | into the :term:`configurator` in ``authentication/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: authentication/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. Create a ``authentication/tutorial/security.py`` module that can find |
| | | our user information by providing an *authentication policy callback*: |
| | | #. Create an ``authentication/tutorial/security.py`` module that can find our |
| | | user information by providing an *authentication policy callback*: |
| | | |
| | | .. literalinclude:: authentication/tutorial/security.py |
| | | :linenos: |
| | |
| | | :language: html |
| | | :linenos: |
| | | |
| | | #. Provide a login/logout box in ``authentication/tutorial/home.pt`` |
| | | #. Provide a login/logout box in ``authentication/tutorial/home.pt``: |
| | | |
| | | .. literalinclude:: authentication/tutorial/home.pt |
| | | :language: html |
| | |
| | | Analysis |
| | | ======== |
| | | |
| | | Unlike many web frameworks, Pyramid includes a built-in but optional |
| | | security model for authentication and authorization. This security |
| | | system is intended to be flexible and support many needs. In this |
| | | security model, authentication (who are you) and authorization (what |
| | | are you allowed to do) are not just pluggable, but de-coupled. To learn |
| | | one step at a time, we provide a system that identifies users and lets |
| | | them log out. |
| | | Unlike many web frameworks, Pyramid includes a built-in but optional security |
| | | model for authentication and authorization. This security system is intended to |
| | | be flexible and support many needs. In this security model, authentication (who |
| | | are you) and authorization (what are you allowed to do) are not just pluggable, |
| | | but de-coupled. To learn one step at a time, we provide a system that |
| | | identifies users and lets them log out. |
| | | |
| | | In this example we chose to use the bundled |
| | | :ref:`AuthTktAuthenticationPolicy <authentication_module>` |
| | | policy. We enabled it in our configuration and provided a |
| | | ticket-signing secret in our INI file. |
| | | In this example we chose to use the bundled :ref:`AuthTktAuthenticationPolicy |
| | | <authentication_module>` policy. We enabled it in our configuration and |
| | | provided a ticket-signing secret in our INI file. |
| | | |
| | | Our view class grew a login view. When you reached it via a GET, |
| | | it returned a login form. When reached via POST, it processed the |
| | | username and password against the "groupfinder" callable that we |
| | | registered in the configuration. |
| | | Our view class grew a login view. When you reached it via a ``GET`` request, it |
| | | returned a login form. When reached via ``POST``, it processed the submitted |
| | | username and password against the "groupfinder" callable that we registered in |
| | | the configuration. |
| | | |
| | | In our template, we fetched the ``logged_in`` value from the view |
| | | class. We use this to calculate the logged-in user, |
| | | if any. In the template we can then choose to show a login link to |
| | | anonymous visitors or a logout link to logged-in users. |
| | | In our template, we fetched the ``logged_in`` value from the view class. We use |
| | | this to calculate the logged-in user, if any. In the template we can then |
| | | choose to show a login link to anonymous visitors or a logout link to logged-in |
| | | users. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. What is the difference between a user and a principal? |
| | | |
| | | #. Can I use a database behind my ``groupfinder`` to look up principals? |
| | | |
| | | #. Once I am logged in, does any user-centric information get jammed |
| | | onto each request? Use ``import pdb; pdb.set_trace()`` to answer |
| | | this. |
| | | #. Once I am logged in, does any user-centric information get jammed onto each |
| | | request? Use ``import pdb; pdb.set_trace()`` to answer this. |
| | | |
| | | .. seealso:: See also :ref:`security_chapter`, |
| | | :ref:`AuthTktAuthenticationPolicy <authentication_module>`. |
| | |
| | | 21: Protecting Resources With Authorization |
| | | =========================================== |
| | | |
| | | Assign security statements to resources describing the permissions |
| | | required to perform an operation. |
| | | Assign security statements to resources describing the permissions required to |
| | | perform an operation. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Our application has URLs that allow people to add/edit/delete content |
| | | via a web browser. Time to add security to the application. Let's |
| | | protect our add/edit views to require a login (username of |
| | | ``editor`` and password of ``editor``). We will allow the other views |
| | | to continue working without a password. |
| | | Our application has URLs that allow people to add/edit/delete content via a web |
| | | browser. Time to add security to the application. Let's protect our add/edit |
| | | views to require a login (username of ``editor`` and password of ``editor``). |
| | | We will allow the other views to continue working without a password. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Introduce the Pyramid concepts of authentication, authorization, |
| | | permissions, and access control lists (ACLs) |
| | | - Introduce the Pyramid concepts of authentication, authorization, permissions, |
| | | and access control lists (ACLs). |
| | | |
| | | - Make a :term:`root factory` that returns an instance of our |
| | | class for the top of the application |
| | | - Make a :term:`root factory` that returns an instance of our class for the top |
| | | of the application. |
| | | |
| | | - Assign security statements to our root resource |
| | | - Assign security statements to our root resource. |
| | | |
| | | - Add a permissions predicate on a view |
| | | - Add a permissions predicate on a view. |
| | | |
| | | - Provide a :term:`Forbidden view` to handle visiting a URL without |
| | | adequate permissions |
| | | - Provide a :term:`Forbidden view` to handle visiting a URL without adequate |
| | | permissions. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | $ cd ..; cp -r authentication authorization; cd authorization |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Start by changing ``authorization/tutorial/__init__.py`` to |
| | | specify a root factory to the :term:`configurator`: |
| | | #. Start by changing ``authorization/tutorial/__init__.py`` to specify a root |
| | | factory to the :term:`configurator`: |
| | | |
| | | .. literalinclude:: authorization/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. That means we need to implement |
| | | ``authorization/tutorial/resources.py`` |
| | | #. That means we need to implement ``authorization/tutorial/resources.py``: |
| | | |
| | | .. literalinclude:: authorization/tutorial/resources.py |
| | | :linenos: |
| | |
| | | |
| | | #. If you are still logged in, click the "Log Out" link. |
| | | |
| | | #. Visit http://localhost:6543/howdy in a browser. You should be |
| | | asked to login. |
| | | #. Visit http://localhost:6543/howdy in a browser. You should be asked to |
| | | login. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | This simple tutorial step can be boiled down to the following: |
| | | |
| | | - A view can require a *permission* (``edit``) |
| | | - A view can require a *permission* (``edit``). |
| | | |
| | | - The context for our view (the ``Root``) has an access control list |
| | | (ACL) |
| | | - The context for our view (the ``Root``) has an access control list (ACL). |
| | | |
| | | - This ACL says that the ``edit`` permission is available on ``Root`` |
| | | to the ``group:editors`` *principal* |
| | | - This ACL says that the ``edit`` permission is available on ``Root`` to the |
| | | ``group:editors`` *principal*. |
| | | |
| | | - The registered ``groupfinder`` answers whether a particular user |
| | | (``editor``) has a particular group (``group:editors``) |
| | | - The registered ``groupfinder`` answers whether a particular user (``editor``) |
| | | has a particular group (``group:editors``). |
| | | |
| | | In summary: ``hello`` wants ``edit`` permission, ``Root`` says |
| | | In summary, ``hello`` wants ``edit`` permission, ``Root`` says |
| | | ``group:editors`` has ``edit`` permission. |
| | | |
| | | Of course, this only applies on ``Root``. Some other part of the site |
| | | (a.k.a. *context*) might have a different ACL. |
| | | Of course, this only applies on ``Root``. Some other part of the site (a.k.a. |
| | | *context*) might have a different ACL. |
| | | |
| | | If you are not logged in and visit ``/howdy``, you need to get |
| | | shown the login screen. How does Pyramid know what is the login page to |
| | | use? We explicitly told Pyramid that the ``login`` view should be used |
| | | by decorating the view with ``@forbidden_view_config``. |
| | | If you are not logged in and visit ``/howdy``, you need to get shown the login |
| | | screen. How does Pyramid know what is the login page to use? We explicitly told |
| | | Pyramid that the ``login`` view should be used by decorating the view with |
| | | ``@forbidden_view_config``. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` |
| | | decorator? |
| | | #. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` decorator? |
| | | |
| | | #. Perhaps you would like the experience of not having enough permissions |
| | | (forbidden) to be richer. How could you change this? |
| | | |
| | | #. Perhaps we want to store security statements in a database and |
| | | allow editing via a browser. How might this be done? |
| | | #. Perhaps we want to store security statements in a database and allow editing |
| | | via a browser. How might this be done? |
| | | |
| | | #. What if we want different security statements on different kinds of |
| | | objects? Or on the same kinds of objects, but in different parts of a |
| | | URL hierarchy? |
| | | #. What if we want different security statements on different kinds of objects? |
| | | Or on the same kinds of objects, but in different parts of a URL hierarchy? |
| | |
| | | 19: Databases Using SQLAlchemy |
| | | ============================== |
| | | |
| | | Store/retrieve data using the SQLAlchemy ORM atop the SQLite database. |
| | | Store and retrieve data using the SQLAlchemy ORM atop the SQLite database. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Our Pyramid-based wiki application now needs database-backed storage of |
| | | pages. This frequently means a SQL database. The Pyramid community |
| | | strongly supports the |
| | | :ref:`SQLAlchemy <sqla:index_toplevel>` project and its |
| | | :ref:`object-relational mapper (ORM) <sqla:ormtutorial_toplevel>` |
| | | as a convenient, Pythonic way to interface to databases. |
| | | Our Pyramid-based wiki application now needs database-backed storage of pages. |
| | | This frequently means an SQL database. The Pyramid community strongly supports |
| | | the :ref:`SQLAlchemy <sqla:index_toplevel>` project and its |
| | | :ref:`object-relational mapper (ORM) <sqla:ormtutorial_toplevel>` as a |
| | | convenient, Pythonic way to interface to databases. |
| | | |
| | | In this step we hook up SQLAlchemy to a SQLite database table, |
| | | providing storage and retrieval for the wikipages in the previous step. |
| | | In this step we hook up SQLAlchemy to a SQLite database table, providing |
| | | storage and retrieval for the wiki pages in the previous step. |
| | | |
| | | .. note:: |
| | | |
| | | The ``alchemy`` scaffold is really helpful for getting a |
| | | SQLAlchemy project going, including generation of the console |
| | | script. Since we want to see all the decisions, we will forgo |
| | | convenience in this tutorial and wire it up ourselves. |
| | | The ``alchemy`` scaffold is really helpful for getting an SQLAlchemy |
| | | project going, including generation of the console script. Since we want to |
| | | see all the decisions, we will forgo convenience in this tutorial, and wire |
| | | it up ourselves. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Store pages in SQLite by using SQLAlchemy models |
| | | - Store pages in SQLite by using SQLAlchemy models. |
| | | |
| | | - Use SQLAlchemy queries to list/add/view/edit pages |
| | | - Use SQLAlchemy queries to list/add/view/edit pages. |
| | | |
| | | - Provide a database-initialize command by writing a Pyramid *console |
| | | script* which can be run from the command line |
| | | - Provide a database-initialize command by writing a Pyramid *console script* |
| | | which can be run from the command line. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | $ cd ..; cp -r forms databases; cd databases |
| | | |
| | | #. We need to add some dependencies in ``databases/setup.py`` as well |
| | | as an "entry point" for the command-line script: |
| | | #. We need to add some dependencies in ``databases/setup.py`` as well as an |
| | | "entry point" for the command-line script: |
| | | |
| | | .. literalinclude:: databases/setup.py |
| | | :linenos: |
| | | |
| | | .. note:: |
| | | |
| | | We aren't yet doing ``$VENV/bin/pip install -e .`` as we |
| | | will change it later. |
| | | We aren't yet doing ``$VENV/bin/pip install -e .`` as we will change it |
| | | later. |
| | | |
| | | #. Our configuration file at ``databases/development.ini`` wires |
| | | together some new pieces: |
| | | #. Our configuration file at ``databases/development.ini`` wires together some |
| | | new pieces: |
| | | |
| | | .. literalinclude:: databases/development.ini |
| | | :language: ini |
| | | |
| | | #. This engine configuration now needs to be read into the application |
| | | through changes in ``databases/tutorial/__init__.py``: |
| | | #. This engine configuration now needs to be read into the application through |
| | | changes in ``databases/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: databases/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. Make a command-line script at ``databases/tutorial/initialize_db.py`` |
| | | to initialize the database: |
| | | #. Make a command-line script at ``databases/tutorial/initialize_db.py`` to |
| | | initialize the database: |
| | | |
| | | .. literalinclude:: databases/tutorial/initialize_db.py |
| | | :linenos: |
| | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/initialize_tutorial_db development.ini |
| | | 2015-06-01 11:22:52,650 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2015-06-01 11:22:52,650 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2015-06-01 11:22:52,651 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2015-06-01 11:22:52,651 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2015-06-01 11:22:52,652 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages") |
| | | 2015-06-01 11:22:52,652 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2015-06-01 11:22:52,653 INFO [sqlalchemy.engine.base.Engine][MainThread] |
| | | |
| | | 2016-04-16 13:01:33,055 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-16 13:01:33,055 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2016-04-16 13:01:33,056 INFO [sqlalchemy.engine.base.Engine][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 |
| | | 2016-04-16 13:01:33,056 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2016-04-16 13:01:33,057 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages") |
| | | 2016-04-16 13:01:33,057 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2016-04-16 13:01:33,058 INFO [sqlalchemy.engine.base.Engine][MainThread] |
| | | CREATE TABLE wikipages ( |
| | | uid INTEGER NOT NULL, |
| | | title TEXT, |
| | | body TEXT, |
| | | PRIMARY KEY (uid), |
| | | UNIQUE (title) |
| | | uid INTEGER NOT NULL, |
| | | title TEXT, |
| | | body TEXT, |
| | | PRIMARY KEY (uid), |
| | | UNIQUE (title) |
| | | ) |
| | | |
| | | |
| | | 2015-06-01 11:22:52,653 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2015-06-01 11:22:52,655 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT |
| | | 2015-06-01 11:22:52,658 INFO [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit) |
| | | 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?) |
| | | 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] ('Root', '<p>Root</p>') |
| | | 2015-06-01 11:22:52,659 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT |
| | | 2016-04-16 13:01:33,058 INFO [sqlalchemy.engine.base.Engine][MainThread] () |
| | | 2016-04-16 13:01:33,059 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT |
| | | 2016-04-16 13:01:33,062 INFO [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit) |
| | | 2016-04-16 13:01:33,062 INFO [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?) |
| | | 2016-04-16 13:01:33,063 INFO [sqlalchemy.engine.base.Engine][MainThread] ('Root', '<p>Root</p>') |
| | | 2016-04-16 13:01:33,063 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT |
| | | |
| | | #. With our data now driven by SQLAlchemy queries, we need to update |
| | | our ``databases/tutorial/views.py``: |
| | | #. With our data now driven by SQLAlchemy queries, we need to update our |
| | | ``databases/tutorial/views.py``: |
| | | |
| | | .. literalinclude:: databases/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. Our tests in ``databases/tutorial/tests.py`` changed to include |
| | | SQLAlchemy bootstrapping: |
| | | #. Our tests in ``databases/tutorial/tests.py`` changed to include SQLAlchemy |
| | | bootstrapping: |
| | | |
| | | .. literalinclude:: databases/tutorial/tests.py |
| | | :linenos: |
| | | |
| | | #. Run the tests in your package using ``nose``: |
| | | #. Run the tests in your package using ``py.test``: |
| | | |
| | | .. code-block:: bash |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | .. |
| | | ----------------------------------------------------------------- |
| | | Ran 2 tests in 1.141s |
| | | |
| | | OK |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .. |
| | | 2 passed in 1.41 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | #. Open http://localhost:6543/ in a browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Let's start with the dependencies. We made the decision to use |
| | | ``SQLAlchemy`` to talk to our database. We also, though, installed |
| | | ``pyramid_tm`` and ``zope.sqlalchemy``. Why? |
| | | Let's start with the dependencies. We made the decision to use ``SQLAlchemy`` |
| | | to talk to our database. We also, though, installed ``pyramid_tm`` and |
| | | ``zope.sqlalchemy``. Why? |
| | | |
| | | Pyramid has a strong orientation towards support for ``transactions``. |
| | | Specifically, you can install a transaction manager into your |
| | | application either as middleware or a Pyramid "tween". Then, |
| | | just before you return the response, all transaction-aware parts of |
| | | your application are executed. |
| | | Specifically, you can install a transaction manager into your application |
| | | either as middleware or a Pyramid "tween". Then, just before you return the |
| | | response, all transaction-aware parts of your application are executed. |
| | | |
| | | This means Pyramid view code usually doesn't manage transactions. If |
| | | your view code or a template generates an error, the transaction manager |
| | | aborts the transaction. This is a very liberating way to write code. |
| | | This means Pyramid view code usually doesn't manage transactions. If your view |
| | | code or a template generates an error, the transaction manager aborts the |
| | | transaction. This is a very liberating way to write code. |
| | | |
| | | The ``pyramid_tm`` package provides a "tween" that is configured in the |
| | | ``development.ini`` configuration file. That installs it. We then need |
| | | a package that makes SQLAlchemy, and thus the RDBMS transaction manager, |
| | | integrate with the Pyramid transaction manager. That's what |
| | | ``zope.sqlalchemy`` does. |
| | | ``development.ini`` configuration file. That installs it. We then need a |
| | | package that makes SQLAlchemy, and thus the RDBMS transaction manager, |
| | | integrate with the Pyramid transaction manager. That's what ``zope.sqlalchemy`` |
| | | does. |
| | | |
| | | Where do we point at the location on disk for the SQLite file? In the |
| | | configuration file. This lets consumers of our package change the |
| | | location in a safe (non-code) way. That is, in configuration. This |
| | | configuration-oriented approach isn't required in Pyramid; you can |
| | | still make such statements in your ``__init__.py`` or some companion |
| | | module. |
| | | configuration file. This lets consumers of our package change the location in a |
| | | safe (non-code) way. That is, in configuration. This configuration-oriented |
| | | approach isn't required in Pyramid; you can still make such statements in your |
| | | ``__init__.py`` or some companion module. |
| | | |
| | | The ``initialize_tutorial_db`` is a nice example of framework support. |
| | | You point your setup at the location of some ``[console_scripts]`` and |
| | | these get generated into your virtual environment's ``bin`` directory. Our |
| | | console script follows the pattern of being fed a configuration file |
| | | with all the bootstrapping. It then opens SQLAlchemy and creates the |
| | | root of the wiki, which also makes the SQLite file. Note the |
| | | ``with transaction.manager`` part that puts the work in the scope of a |
| | | transaction, as we aren't inside a web request where this is done |
| | | automatically. |
| | | The ``initialize_tutorial_db`` is a nice example of framework support. You |
| | | point your setup at the location of some ``[console_scripts]``, and these get |
| | | generated into your virtual environment's ``bin`` directory. Our console script |
| | | follows the pattern of being fed a configuration file with all the |
| | | bootstrapping. It then opens SQLAlchemy and creates the root of the wiki, which |
| | | also makes the SQLite file. Note the ``with transaction.manager`` part that |
| | | puts the work in the scope of a transaction, as we aren't inside a web request |
| | | where this is done automatically. |
| | | |
| | | The ``models.py`` does a little bit extra work to hook up SQLAlchemy |
| | | into the Pyramid transaction manager. It then declares the model for a |
| | | ``Page``. |
| | | The ``models.py`` does a little bit of extra work to hook up SQLAlchemy into |
| | | the Pyramid transaction manager. It then declares the model for a ``Page``. |
| | | |
| | | Our views have changes primarily around replacing our dummy |
| | | dictionary-of-dictionaries data with proper database support: list the |
| | | rows, add a row, edit a row, and delete a row. |
| | | dictionary-of-dictionaries data with proper database support: list the rows, |
| | | add a row, edit a row, and delete a row. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Why all this code? Why can't I just type 2 lines and have magic ensue? |
| | | #. Why all this code? Why can't I just type two lines and have magic ensue? |
| | | |
| | | #. Give a try at a button that deletes a wiki page. |
| | |
| | | |
| | | Error handling and introspection using the ``pyramid_debugtoolbar`` add-on. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | As we introduce the basics we also want to show how to be productive in |
| | | development and debugging. For example, we just discussed template |
| | | reloading and earlier we showed ``--reload`` for application reloading. |
| | | As we introduce the basics, we also want to show how to be productive in |
| | | development and debugging. For example, we just discussed template reloading, |
| | | and earlier we showed ``--reload`` for application reloading. |
| | | |
| | | ``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes |
| | | several tools available in your browser. Adding it to your project |
| | | illustrates several points about configuration. |
| | | ``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes several tools |
| | | available in your browser. Adding it to your project illustrates several points |
| | | about configuration. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Install and enable the toolbar to help during development |
| | | - Install and enable the toolbar to help during development. |
| | | |
| | | - Explain Pyramid add-ons |
| | | - Explain Pyramid add-ons. |
| | | |
| | | - Show how an add-on gets configured into your application |
| | | - Show how an add-on gets configured into your application. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. First we copy the results of the previous step, as well as install |
| | | the ``pyramid_debugtoolbar`` package: |
| | | #. First we copy the results of the previous step, as well as install the |
| | | ``pyramid_debugtoolbar`` package: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | #. Open http://localhost:6543/ in your browser. See the handy |
| | | toolbar on the right. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | |
| | | means we need to include its add-on configuration into our web application. We |
| | | could do this with imperative configuration in ``tutorial/__init__.py`` by |
| | | using ``config.include``. Pyramid also supports wiring in add-on configuration |
| | | via our ``development.ini`` using ``pyramid.includes``. We use this to load |
| | | the configuration for the debugtoolbar. |
| | | via our ``development.ini`` using ``pyramid.includes``. We use this to load the |
| | | configuration for the debugtoolbar. |
| | | |
| | | You'll now see an attractive button on the right side of your browser, which |
| | | you may click to provide introspective access to debugging information in a |
| | | new browser tab. Even better, if your web application generates an error, you |
| | | will see a nice traceback on the screen. When you want to disable this |
| | | toolbar, there's no need to change code: you can remove it from |
| | | ``pyramid.includes`` in the relevant ``.ini`` configuration file (thus showing |
| | | why configuration files are handy.) |
| | | you may click to provide introspective access to debugging information in a new |
| | | rowser tab. Even better, if your web application generates an error, you will |
| | | see a nice traceback on the screen. When you want to disable this toolbar, |
| | | there's no need to change code: you can remove it from ``pyramid.includes`` in |
| | | the relevant ``.ini`` configuration file (thus showing why configuration files |
| | | are handy). |
| | | |
| | | Note that the toolbar injects a small amount of HTML/CSS into your app just |
| | | before the closing ``</body>`` tag in order to display itself. If you start to |
| | |
| | | |
| | | .. seealso:: See also :ref:`pyramid_debugtoolbar <toolbar:overview>`. |
| | | |
| | | |
| | | Extra Credit |
| | | ============ |
| | | |
| | | #. Why don't we add ``pyramid_debugtoolbar`` to the list of |
| | | ``install_requires`` dependencies in ``debugtoolbar/setup.py``? |
| | | |
| | | #. Introduce a bug into your application: Change: |
| | | #. Introduce a bug into your application. Change: |
| | | |
| | | .. code-block:: python |
| | | |
| | |
| | | def hello_world(request): |
| | | return xResponse('<body><h1>Hello World!</h1></body>') |
| | | |
| | | Save, and visit http://localhost:6543/ again. Notice the nice |
| | | traceback display. On the lowest line, click the "screen" icon to the |
| | | right, and try typing the variable names ``request`` and ``Response``. |
| | | What else can you discover? |
| | | Save, and visit http://localhost:6543/ again. Notice the nice traceback |
| | | display. On the lowest line, click the "screen" icon to the right, and try |
| | | typing the variable names ``request`` and ``Response``. What else can you |
| | | discover? |
| | |
| | | |
| | | Schema-driven, autogenerated forms with validation. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Modern web applications deal extensively with forms. Developers, |
| | | though, have a wide range of philosophies about how frameworks should |
| | | help them with their forms. As such, Pyramid doesn't directly bundle |
| | | one particular form library. Instead there are a variety of form |
| | | libraries that are easy to use in Pyramid. |
| | | Modern web applications deal extensively with forms. Developers, though, have a |
| | | wide range of philosophies about how frameworks should help them with their |
| | | forms. As such, Pyramid doesn't directly bundle one particular form library. |
| | | Instead there are a variety of form libraries that are easy to use in Pyramid. |
| | | |
| | | :ref:`Deform <deform:overview>` |
| | | is one such library. In this step, we introduce Deform for our |
| | | forms and validation. This also gives us :ref:`Colander <colander:overview>` |
| | | :ref:`Deform <deform:overview>` is one such library. In this step, we introduce |
| | | Deform for our forms. This also gives us :ref:`Colander <colander:overview>` |
| | | for schemas and validation. |
| | | |
| | | Deform is getting a facelift, with styling from Twitter Bootstrap and |
| | | advanced widgets from popular JavaScript projects. The work began in |
| | | ``deform_bootstrap`` and is being merged into an update to Deform. |
| | | Deform uses styling from Twitter Bootstrap and advanced widgets from popular |
| | | JavaScript projects. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Make a schema using Colander, the companion to Deform |
| | | - Make a schema using Colander, the companion to Deform. |
| | | |
| | | - Create a form with Deform and change our views to handle validation |
| | | - Create a form with Deform and change our views to handle validation. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | $ cd ..; cp -r view_classes forms; cd forms |
| | | |
| | | #. Let's edit ``forms/setup.py`` to declare a dependency on Deform |
| | | (which then pulls in Colander as a dependency: |
| | | #. Let's edit ``forms/setup.py`` to declare a dependency on Deform (which then |
| | | pulls in Colander as a dependency: |
| | | |
| | | .. literalinclude:: forms/setup.py |
| | | :linenos: |
| | |
| | | |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Register a static view in ``forms/tutorial/__init__.py`` for |
| | | Deform's CSS/JS etc. as well as our demo wikipage scenario's |
| | | views: |
| | | #. Register a static view in ``forms/tutorial/__init__.py`` for Deform's CSS, |
| | | JavaScript, etc., as well as our demo wiki page's views: |
| | | |
| | | .. literalinclude:: forms/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. Implement the new views, as well as the form schemas and some |
| | | dummy data, in ``forms/tutorial/views.py``: |
| | | #. Implement the new views, as well as the form schemas and some dummy data, in |
| | | ``forms/tutorial/views.py``: |
| | | |
| | | .. literalinclude:: forms/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. A template for the top of the "wiki" in |
| | | ``forms/tutorial/wiki_view.pt``: |
| | | #. A template for the top of the "wiki" in ``forms/tutorial/wiki_view.pt``: |
| | | |
| | | .. literalinclude:: forms/tutorial/wiki_view.pt |
| | | :language: html |
| | |
| | | :language: html |
| | | :linenos: |
| | | |
| | | #. Finally, a template at ``forms/tutorial/wikipage_view.pt`` |
| | | for viewing a wiki page: |
| | | #. Finally, a template at ``forms/tutorial/wikipage_view.pt`` for viewing a |
| | | wiki page: |
| | | |
| | | .. literalinclude:: forms/tutorial/wikipage_view.pt |
| | | :language: html |
| | | :linenos: |
| | | |
| | | #. Run the tests: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .. |
| | | 2 passed in 0.45 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | Analysis |
| | | ======== |
| | | |
| | | This step helps illustrate the utility of asset specifications for |
| | | static assets. We have an outside package called Deform with static |
| | | assets which need to be published. We don't have to know where on disk |
| | | it is located. We point at the package, then the path inside the package. |
| | | This step helps illustrate the utility of asset specifications for static |
| | | assets. We have an outside package called Deform with static assets which need |
| | | to be published. We don't have to know where on disk it is located. We point at |
| | | the package, then the path inside the package. |
| | | |
| | | We just need to include a call to ``add_static_view`` to make that |
| | | directory available at a URL. For Pyramid-specific packages, |
| | | Pyramid provides a facility (``config.include()``) which even makes |
| | | that unnecessary for consumers of a package. (Deform is not specific to |
| | | Pyramid.) |
| | | We just need to include a call to ``add_static_view`` to make that directory |
| | | available at a URL. For Pyramid-specific packages, Pyramid provides a facility |
| | | (``config.include()``) which even makes that unnecessary for consumers of a |
| | | package. (Deform is not specific to Pyramid.) |
| | | |
| | | Our forms have rich widgets which need the static CSS and JS just |
| | | mentioned. Deform has a :term:`resource registry` which allows widgets |
| | | to specify which JS and CSS are needed. Our ``wikipage_addedit.pt`` |
| | | template shows how we iterated over that data to generate markup that |
| | | includes the needed resources. |
| | | Our forms have rich widgets which need the static CSS and JavaScript just |
| | | mentioned. Deform has a :term:`resource registry` which allows widgets to |
| | | specify which JavaScript and CSS are needed. Our ``wikipage_addedit.pt`` |
| | | template shows how we iterated over that data to generate markup that includes |
| | | the needed resources. |
| | | |
| | | Our add and edit views use a pattern called *self-posting forms*. |
| | | Meaning, the same URL is used to ``GET`` the form as is used to |
| | | ``POST`` the form. The route, the view, and the template are the same |
| | | whether you are walking up to it the first time or you clicked a button. |
| | | Our add and edit views use a pattern called *self-posting forms*. Meaning, the |
| | | same URL is used to ``GET`` the form as is used to ``POST`` the form. The |
| | | route, the view, and the template are the same URL whether you are walking up |
| | | to it for the first time or you clicked a button. |
| | | |
| | | Inside the view we do ``if 'submit' in self.request.params:`` to see if |
| | | this form was a ``POST`` where the user clicked on a particular button |
| | | Inside the view we do ``if 'submit' in self.request.params:`` to see if this |
| | | form was a ``POST`` where the user clicked on a particular button |
| | | ``<input name="submit">``. |
| | | |
| | | The form controller then follows a typical pattern: |
| | | |
| | | - If you are doing a GET, skip over and just return the form |
| | | - If you are doing a ``GET``, skip over and just return the form. |
| | | |
| | | - If you are doing a POST, validate the form contents |
| | | - If you are doing a ``POST``, validate the form contents. |
| | | |
| | | - If the form is invalid, bail out by re-rendering the form with the |
| | | supplied ``POST`` data |
| | | - If the form is invalid, bail out by re-rendering the form with the supplied |
| | | ``POST`` data. |
| | | |
| | | - If the validation succeeded, perform some action and issue a |
| | | redirect via ``HTTPFound``. |
| | | - If the validation succeeded, perform some action and issue a redirect via |
| | | ``HTTPFound``. |
| | | |
| | | We are, in essence, writing our own form controller. Other |
| | | Pyramid-based systems, including ``pyramid_deform``, provide a |
| | | form-centric view class which automates much of this branching and |
| | | routing. |
| | | We are, in essence, writing our own form controller. Other Pyramid-based |
| | | systems, including ``pyramid_deform``, provide a form-centric view class which |
| | | automates much of this branching and routing. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Give a try at a button that goes to a delete view for a |
| | | particular wiki page. |
| | | #. Give a try at a button that goes to a delete view for a particular wiki |
| | | page. |
| | |
| | | |
| | | Write end-to-end full-stack testing using ``webtest``. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Unit tests are a common and popular approach to test-driven development |
| | | (TDD). In web applications, though, the templating and entire apparatus |
| | | of a web site are important parts of the delivered quality. We'd like a |
| | | way to test these. |
| | | Unit tests are a common and popular approach to test-driven development (TDD). |
| | | In web applications, though, the templating and entire apparatus of a web site |
| | | are important parts of the delivered quality. We'd like a way to test these. |
| | | |
| | | WebTest is a Python package that does functional testing. With WebTest |
| | | you can write tests which simulate a full HTTP request against a WSGI |
| | | application, then test the information in the response. For speed |
| | | purposes, WebTest skips the setup/teardown of an actual HTTP server, |
| | | providing tests that run fast enough to be part of TDD. |
| | | `WebTest <http://docs.pylonsproject.org/projects/webtest/en/latest/>`_ is a |
| | | Python package that does functional testing. With WebTest you can write tests |
| | | which simulate a full HTTP request against a WSGI application, then test the |
| | | information in the response. For speed purposes, WebTest skips the |
| | | setup/teardown of an actual HTTP server, providing tests that run fast enough |
| | | to be part of TDD. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Write a test which checks the contents of the returned HTML |
| | | - Write a test which checks the contents of the returned HTML. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. First we copy the results of the previous step, as well as install |
| | | the ``webtest`` package: |
| | | #. First we copy the results of the previous step, as well as install the |
| | | ``webtest`` package: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | .. literalinclude:: functional_testing/tutorial/tests.py |
| | | :linenos: |
| | | |
| | | Be sure this file is not executable, or ``nosetests`` may not |
| | | include your tests. |
| | | Be sure this file is not executable, or ``pytest`` may not include your |
| | | tests. |
| | | |
| | | #. Now run the tests: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .. |
| | | 2 passed in 0.25 seconds |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 2 tests in 0.141s |
| | | |
| | | OK |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | We now have the end-to-end testing we were looking for. WebTest lets us |
| | | simply extend our existing ``nose``-based test approach with functional |
| | | tests that are reported in the same output. These new tests not only |
| | | cover our templating, but they didn't dramatically increase the |
| | | execution time of our tests. |
| | | We now have the end-to-end testing we were looking for. WebTest lets us simply |
| | | extend our existing ``pytest``-based test approach with functional tests that |
| | | are reported in the same output. These new tests not only cover our templating, |
| | | but they didn't dramatically increase the execution time of our tests. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Why do our functional tests use ``b''``? |
| | |
| | | 01: Single-File Web Applications |
| | | ================================ |
| | | |
| | | What's the simplest way to get started in Pyramid? A single-file module. |
| | | No Python packages, no ``pip install -e .``, no other machinery. |
| | | What's the simplest way to get started in Pyramid? A single-file module. No |
| | | Python packages, no ``pip install -e .``, no other machinery. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Microframeworks are all the rage these days. "Microframework" is a |
| | | marketing term, not a technical one. They have a low mental overhead: |
| | | they do so little, the only things you have to worry about are *your |
| | | things*. |
| | | Microframeworks are all the rage these days. "Microframework" is a marketing |
| | | term, not a technical one. They have a low mental overhead: they do so little, |
| | | the only things you have to worry about are *your things*. |
| | | |
| | | Pyramid is special because it can act as a single-file module |
| | | microframework. You can have a single Python file that can be executed |
| | | directly by Python. But Pyramid also provides facilities to scale to |
| | | the largest of applications. |
| | | Pyramid is special because it can act as a single-file module microframework. |
| | | You can have a single Python file that can be executed directly by Python. But |
| | | Pyramid also provides facilities to scale to the largest of applications. |
| | | |
| | | Python has a standard called :term:`WSGI` that defines how |
| | | Python web applications plug into standard servers, getting passed |
| | | incoming requests and returning responses. Most modern Python web |
| | | frameworks obey an "MVC" (model-view-controller) application pattern, |
| | | where the data in the model has a view that mediates interaction with |
| | | outside systems. |
| | | Python has a standard called :term:`WSGI` that defines how Python web |
| | | applications plug into standard servers, getting passed incoming requests, and |
| | | returning responses. Most modern Python web frameworks obey an "MVC" |
| | | (model-view-controller) application pattern, where the data in the model has a |
| | | view that mediates interaction with outside systems. |
| | | |
| | | In this step we'll see a brief glimpse of WSGI servers, WSGI |
| | | applications, requests, responses, and views. |
| | | In this step we'll see a brief glimpse of WSGI servers, WSGI applications, |
| | | requests, responses, and views. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Get a running Pyramid web application, as simply as possible |
| | | - Get a running Pyramid web application, as simply as possible. |
| | | |
| | | - Use that as a well-understood base for adding each unit of complexity |
| | | - Use that as a well-understood base for adding each unit of complexity. |
| | | |
| | | - Initial exposure to WSGI apps, requests, views, and responses |
| | | - Initial exposure to WSGI apps, requests, views, and responses. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | #. Open http://localhost:6543/ in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | New to Python web programming? If so, some lines in module merit |
| | | New to Python web programming? If so, some lines in the module merit |
| | | explanation: |
| | | |
| | | #. *Line 11*. The ``if __name__ == '__main__':`` is Python's way of |
| | | saying "Start here when running from the command line", rather than |
| | | when this module is imported. |
| | | #. *Line 11*. The ``if __name__ == '__main__':`` is Python's way of saying, |
| | | "Start here when running from the command line", rather than when this |
| | | module is imported. |
| | | |
| | | #. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect |
| | | :term:`view` code to a particular URL :term:`route`. |
| | | #. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect :term:`view` |
| | | code to a particular URL :term:`route`. |
| | | |
| | | #. *Lines 6-8*. Implement the view code that generates the |
| | | :term:`response`. |
| | | #. *Lines 6-8*. Implement the view code that generates the :term:`response`. |
| | | |
| | | #. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP |
| | | server. |
| | | #. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP server. |
| | | |
| | | As shown in this example, the :term:`configurator` plays a |
| | | central role in Pyramid development. Building an application from |
| | | loosely-coupled parts via :ref:`configuration_narr` is a |
| | | central idea in Pyramid, one that we will revisit regularly in this |
| | | *Quick Tour*. |
| | | As shown in this example, the :term:`configurator` plays a central role in |
| | | Pyramid development. Building an application from loosely-coupled parts via |
| | | :ref:`configuration_narr` is a central idea in Pyramid, one that we will |
| | | revisit regularly in this *Quick Tutorial*. |
| | | |
| | | |
| | | Extra Credit |
| | | ============ |
| | |
| | | |
| | | #. What happens if you return a string of HTML? A sequence of integers? |
| | | |
| | | #. Put something invalid, such as ``print xyz``, in the view function. |
| | | Kill your ``python app.py`` with ``cntrl-c`` and restart, |
| | | then reload your browser. See the exception in the console? |
| | | #. Put something invalid, such as ``print xyz``, in the view function. Kill |
| | | your ``python app.py`` with ``ctrl-C`` and restart, then reload your |
| | | browser. See the exception in the console? |
| | | |
| | | #. The ``GI`` in ``WSGI`` stands for "Gateway Interface". What web |
| | | standard is this modelled after? |
| | | #. The ``GI`` in ``WSGI`` stands for "Gateway Interface". What web standard is |
| | | this modelled after? |
| | |
| | | Quick Tutorial for Pyramid |
| | | ========================== |
| | | |
| | | Pyramid is a web framework for Python 2 and 3. This tutorial gives a |
| | | Python 3/2-compatible, high-level tour of the major features. |
| | | Pyramid is a web framework for Python 2 and 3. This tutorial gives a Python |
| | | 3/2-compatible, high-level tour of the major features. |
| | | |
| | | This hands-on tutorial covers "a little about a lot": practical |
| | | introductions to the most common facilities. Fun, fast-paced, and most |
| | | certainly not aimed at experts of the Pyramid web framework. |
| | | This hands-on tutorial covers "a little about a lot": practical introductions |
| | | to the most common facilities. Fun, fast-paced, and most certainly not aimed at |
| | | experts of the Pyramid web framework. |
| | | |
| | | Contents |
| | | ======== |
| | |
| | | Use Pyramid's ``pserve`` command with a ``.ini`` configuration file for |
| | | simpler, better application running. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Pyramid has a first-class concept of |
| | | :ref:`configuration <configuration_narr>` distinct from code. |
| | | This approach is optional, but its presence makes it distinct from |
| | | other Python web frameworks. It taps into Python's ``setuptools`` |
| | | library, which establishes conventions for installing and providing |
| | | "entry points" for Python projects. Pyramid uses an entry point to |
| | | let a Pyramid application know where to find the WSGI app. |
| | | Pyramid has a first-class concept of :ref:`configuration <configuration_narr>` |
| | | distinct from code. This approach is optional, but its presence makes it |
| | | distinct from other Python web frameworks. It taps into Python's ``setuptools`` |
| | | library, which establishes conventions for installing and providing "entry |
| | | points" for Python projects. Pyramid uses an entry point to let a Pyramid |
| | | application know where to find the WSGI app. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Modify our ``setup.py`` to have an entry point telling Pyramid the |
| | | location of the WSGI app |
| | | - Modify our ``setup.py`` to have an entry point telling Pyramid the location |
| | | of the WSGI app. |
| | | |
| | | - Create an application driven by a ``.ini`` file |
| | | - Create an application driven by an ``.ini`` file. |
| | | |
| | | - Startup the application with Pyramid's ``pserve`` command |
| | | - Start the application with Pyramid's ``pserve`` command. |
| | | |
| | | - Move code into the package's ``__init__.py`` |
| | | - Move code into the package's ``__init__.py``. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | $ cd ..; cp -r package ini; cd ini |
| | | |
| | | #. Our ``ini/setup.py`` needs a setuptools "entry point" in the |
| | | ``setup()`` function: |
| | | #. Our ``ini/setup.py`` needs a setuptools "entry point" in the ``setup()`` |
| | | function: |
| | | |
| | | .. literalinclude:: ini/setup.py |
| | | :linenos: |
| | | |
| | | #. We can now install our project, thus generating (or re-generating) an |
| | | "egg" at ``ini/tutorial.egg-info``: |
| | | #. We can now install our project, thus generating (or re-generating) an "egg" |
| | | at ``ini/tutorial.egg-info``: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | :language: ini |
| | | :linenos: |
| | | |
| | | #. We can refactor our startup code from the previous step's ``app.py`` |
| | | into ``ini/tutorial/__init__.py``: |
| | | #. We can refactor our startup code from the previous step's ``app.py`` into |
| | | ``ini/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: ini/tutorial/__init__.py |
| | | :linenos: |
| | |
| | | Analysis |
| | | ======== |
| | | |
| | | Our ``development.ini`` file is read by ``pserve`` and serves to |
| | | bootstrap our application. Processing then proceeds as described in |
| | | the Pyramid chapter on |
| | | Our ``development.ini`` file is read by ``pserve`` and serves to bootstrap our |
| | | application. Processing then proceeds as described in the Pyramid chapter on |
| | | :ref:`application startup <startup_chapter>`: |
| | | |
| | | - ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial`` |
| | | - ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial``. |
| | | |
| | | - The projects's ``setup.py`` has defined an "entry point" (lines 9-12) |
| | | for the project "main" entry point of ``tutorial:main`` |
| | | - The projects's ``setup.py`` has defined an "entry point" (lines 9-12) for the |
| | | project's "main" entry point of ``tutorial:main``. |
| | | |
| | | - The ``tutorial`` package's ``__init__`` has a ``main`` function |
| | | - The ``tutorial`` package's ``__init__`` has a ``main`` function. |
| | | |
| | | - This function is invoked, with the values from certain ``.ini`` |
| | | sections passed in |
| | | - This function is invoked, with the values from certain ``.ini`` sections |
| | | passed in. |
| | | |
| | | The ``.ini`` file is also used for two other functions: |
| | | |
| | | - *Configuring the WSGI server*. ``[server:main]`` wires up the choice of |
| | | which WSGI *server* for your WSGI *application*. In this case, we are using |
| | | ``wsgiref`` bundled in the Python library. It also wires up the *port |
| | | number*: ``port = 6543`` tells ``wsgiref`` to listen on port 6543. |
| | | - *Configuring the WSGI server*. ``[server:main]`` wires up the choice of which |
| | | WSGI *server* for your WSGI *application*. In this case, we are using |
| | | ``wsgiref`` bundled in the Python library. It also wires up the *port |
| | | number*: ``port = 6543`` tells ``wsgiref`` to listen on port 6543. |
| | | |
| | | - *Configuring Python logging*. Pyramid uses Python standard logging, which |
| | | needs a number of configuration values. The ``.ini`` serves this function. |
| | |
| | | request. |
| | | |
| | | We moved our startup code from ``app.py`` to the package's |
| | | ``tutorial/__init__.py``. This isn't necessary, |
| | | but it is a common style in Pyramid to take the WSGI app bootstrapping |
| | | out of your module's code and put it in the package's ``__init__.py``. |
| | | ``tutorial/__init__.py``. This isn't necessary, but it is a common style in |
| | | Pyramid to take the WSGI app bootstrapping out of your module's code and put it |
| | | in the package's ``__init__.py``. |
| | | |
| | | The ``pserve`` application runner has a number of command-line arguments |
| | | and options. We are using ``--reload`` which tells ``pserve`` to watch |
| | | the filesystem for changes to relevant code (Python files, the INI file, |
| | | etc.) and, when something changes, restart the application. Very handy |
| | | during development. |
| | | The ``pserve`` application runner has a number of command-line arguments and |
| | | options. We are using ``--reload`` which tells ``pserve`` to watch the |
| | | filesystem for changes to relevant code (Python files, the INI file, etc.) and, |
| | | when something changes, restart the application. Very handy during development. |
| | | |
| | | |
| | | Extra Credit |
| | | ============ |
| | | |
| | | #. If you don't like configuration and/or ``.ini`` files, |
| | | could you do this yourself in Python code? |
| | | #. If you don't like configuration and/or ``.ini`` files, could you do this |
| | | yourself in Python code? |
| | | |
| | | #. Can we have multiple ``.ini`` configuration files for a project? Why |
| | | might you want to do that? |
| | | #. Can we have multiple ``.ini`` configuration files for a project? Why might |
| | | you want to do that? |
| | | |
| | | #. The entry point in ``setup.py`` didn't mention ``__init__.py`` when |
| | | it declared ``tutorial:main`` function. Why not? |
| | | #. The entry point in ``setup.py`` didn't mention ``__init__.py`` when it |
| | | declared ``tutorial:main`` function. Why not? |
| | | |
| | | #. What is the purpose of ``**settings``? What does the ``**`` signify? |
| | | |
| | |
| | | :ref:`what_is_this_pserve_thing`, |
| | | :ref:`environment_chapter`, |
| | | :ref:`paste_chapter` |
| | | |
| | |
| | | 12: Templating With ``jinja2`` |
| | | ============================== |
| | | |
| | | We just said Pyramid doesn't prefer one templating language over |
| | | another. Time to prove it. Jinja2 is a popular templating system, |
| | | used in Flask and modeled after Django's templates. Let's add |
| | | ``pyramid_jinja2``, a Pyramid :term:`add-on` which enables Jinja2 as a |
| | | :term:`renderer` in our Pyramid applications. |
| | | We just said Pyramid doesn't prefer one templating language over another. Time |
| | | to prove it. Jinja2 is a popular templating system, used in Flask and modeled |
| | | after Django's templates. Let's add ``pyramid_jinja2``, a Pyramid |
| | | :term:`add-on` which enables Jinja2 as a :term:`renderer` in our Pyramid |
| | | applications. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Show Pyramid's support for different templating systems |
| | | - Show Pyramid's support for different templating systems. |
| | | |
| | | - Learn about installing Pyramid add-ons |
| | | - Learn about installing Pyramid add-ons. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. In this step let's start by copying the ``view_class`` step's |
| | | directory, and then installing the ``pyramid_jinja2`` add-on. |
| | | #. In this step let's start by copying the ``view_class`` step's directory, |
| | | and then installing the ``pyramid_jinja2`` add-on. |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | $ $VENV/bin/pip install -e . |
| | | $ $VENV/bin/pip install pyramid_jinja2 |
| | | |
| | | #. We need to include ``pyramid_jinja2`` in |
| | | ``jinja2/tutorial/__init__.py``: |
| | | #. We need to include ``pyramid_jinja2`` in ``jinja2/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: jinja2/tutorial/__init__.py |
| | | :linenos: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.40 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | #. Open http://localhost:6543/ in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Getting a Pyramid add-on into Pyramid is simple. First you use normal |
| | | Python package installation tools to install the add-on package into |
| | | your Python. You then tell Pyramid's configurator to run the setup code |
| | | Getting a Pyramid add-on into Pyramid is simple. First you use normal Python |
| | | package installation tools to install the add-on package into your Python |
| | | virtual environment. You then tell Pyramid's configurator to run the setup code |
| | | in the add-on. In this case the setup code told Pyramid to make a new |
| | | "renderer" available that looked for ``.jinja2`` file extensions. |
| | | |
| | | Our view code stayed largely the same. We simply changed the file |
| | | extension on the renderer. For the template, the syntax for Chameleon |
| | | and Jinja2's basic variable insertion is very similar. |
| | | Our view code stayed largely the same. We simply changed the file extension on |
| | | the renderer. For the template, the syntax for Chameleon and Jinja2's basic |
| | | variable insertion is very similar. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Our project now depends on ``pyramid_jinja2``. We installed that |
| | | dependency manually. What is another way we could have made the |
| | | association? |
| | | #. Our project now depends on ``pyramid_jinja2``. We installed that dependency |
| | | manually. What is another way we could have made the association? |
| | | |
| | | #. We used ``config.include`` which is an imperative configuration to get the |
| | | :term:`Configurator` to load ``pyramid_jinja2``'s configuration. |
| | | What is another way could include it into the config? |
| | | :term:`Configurator` to load ``pyramid_jinja2``'s configuration. What is |
| | | another way could include it into the config? |
| | | |
| | | .. seealso:: `Jinja2 homepage <http://jinja.pocoo.org/>`_, |
| | | and |
| | | .. seealso:: `Jinja2 homepage <http://jinja.pocoo.org/>`_, and |
| | | :ref:`pyramid_jinja2 Overview <jinja2:overview>` |
| | |
| | | .. _qtut_json: |
| | | |
| | | ======================================== |
| | | 14: Ajax Development With JSON Renderers |
| | | 14: AJAX Development With JSON Renderers |
| | | ======================================== |
| | | |
| | | Modern web apps are more than rendered HTML. Dynamic pages now use |
| | | JavaScript to update the UI in the browser by requesting server data as |
| | | JSON. Pyramid supports this with a *JSON renderer*. |
| | | Modern web apps are more than rendered HTML. Dynamic pages now use JavaScript |
| | | to update the UI in the browser by requesting server data as JSON. Pyramid |
| | | supports this with a *JSON renderer*. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | As we saw in :doc:`templating`, view declarations can specify a |
| | | renderer. Output from the view is then run through the renderer, |
| | | which generates and returns the ``Response``. We first used a Chameleon |
| | | renderer, then a Jinja2 renderer. |
| | | As we saw in :doc:`templating`, view declarations can specify a renderer. |
| | | Output from the view is then run through the renderer, which generates and |
| | | returns the response. We first used a Chameleon renderer, then a Jinja2 |
| | | renderer. |
| | | |
| | | Renderers aren't limited, however, to templates that generate HTML. |
| | | Pyramid supplies a JSON renderer which takes Python data, |
| | | serializes it to JSON, and performs some other functions such as |
| | | setting the content type. In fact, you can write your own renderer (or |
| | | extend a built-in renderer) containing custom logic for your unique |
| | | application. |
| | | Renderers aren't limited, however, to templates that generate HTML. Pyramid |
| | | supplies a JSON renderer which takes Python data, serializes it to JSON, and |
| | | performs some other functions such as setting the content type. In fact you can |
| | | write your own renderer (or extend a built-in renderer) containing custom logic |
| | | for your unique application. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | $ cd ..; cp -r view_classes json; cd json |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. We add a new route for ``hello_json`` in |
| | | ``json/tutorial/__init__.py``: |
| | | #. We add a new route for ``hello_json`` in ``json/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: json/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. Rather than implement a new view, we will "stack" another decorator |
| | | on the ``hello`` view in ``views.py``: |
| | | #. Rather than implement a new view, we will "stack" another decorator on the |
| | | ``hello`` view in ``views.py``: |
| | | |
| | | .. literalinclude:: json/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. We need a new functional test at the end of |
| | | ``json/tutorial/tests.py``: |
| | | #. We need a new functional test at the end of ``json/tutorial/tests.py``: |
| | | |
| | | .. literalinclude:: json/tutorial/tests.py |
| | | :linenos: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | ..... |
| | | 5 passed in 0.47 seconds |
| | | |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/howdy.json in your browser and you |
| | | will see the resulting JSON response. |
| | | #. Open http://localhost:6543/howdy.json in your browser and you will see the |
| | | resulting JSON response. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Earlier we changed our view functions and methods to return Python |
| | | data. This change to a data-oriented view layer made test writing |
| | | easier, decoupling the templating from the view logic. |
| | | Earlier we changed our view functions and methods to return Python data. This |
| | | change to a data-oriented view layer made test writing easier, decoupling the |
| | | templating from the view logic. |
| | | |
| | | Since Pyramid has a JSON renderer as well as the templating renderers, |
| | | it is an easy step to return JSON. In this case we kept the exact same |
| | | view and arranged to return a JSON encoding of the view data. We did |
| | | this by: |
| | | Since Pyramid has a JSON renderer as well as the templating renderers, it is an |
| | | easy step to return JSON. In this case we kept the exact same view and arranged |
| | | to return a JSON encoding of the view data. We did this by: |
| | | |
| | | - Adding a route to map ``/howdy.json`` to a route name |
| | | - Adding a route to map ``/howdy.json`` to a route name. |
| | | |
| | | - Providing a ``@view_config`` that associated that route name with an |
| | | existing view |
| | | - Providing a ``@view_config`` that associated that route name with an existing |
| | | view. |
| | | |
| | | - *overriding* the view defaults in the view config that mentions the |
| | | ``hello_json`` route, so that when the route is matched, we use the JSON |
| | | - *Overriding* the view defaults in the view config that mentions the |
| | | ``hello_json`` route, so that when the route is matched, we use the JSON |
| | | renderer rather than the ``home.pt`` template renderer that would otherwise |
| | | be used. |
| | | |
| | | In fact, for pure Ajax-style web applications, we could re-use the existing |
| | | route by using Pyramid's view predicates to match on the |
| | | ``Accepts:`` header sent by modern Ajax implementation. |
| | | In fact, for pure AJAX-style web applications, we could re-use the existing |
| | | route by using Pyramid's view predicates to match on the ``Accepts:`` header |
| | | sent by modern AJAX implementations. |
| | | |
| | | Pyramid's JSON renderer uses the base Python JSON encoder, |
| | | thus inheriting its strengths and weaknesses. For example, |
| | | Python can't natively JSON encode DateTime objects. There are a number |
| | | of solutions for this in Pyramid, including extending the JSON renderer |
| | | with a custom renderer. |
| | | Pyramid's JSON renderer uses the base Python JSON encoder, thus inheriting its |
| | | strengths and weaknesses. For example, Python can't natively JSON encode |
| | | DateTime objects. There are a number of solutions for this in Pyramid, |
| | | including extending the JSON renderer with a custom renderer. |
| | | |
| | | .. seealso:: :ref:`views_which_use_a_renderer`, |
| | | :ref:`json_renderer`, and |
| | |
| | | 16: Collecting Application Info With Logging |
| | | ============================================ |
| | | |
| | | Capture debugging and error output from your web applications using |
| | | standard Python logging. |
| | | Capture debugging and error output from your web applications using standard |
| | | Python logging. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | It's important to know what is going on inside our web application. |
| | | In development we might need to collect some output. In production, |
| | | we might need to detect problems when other people use the site. We |
| | | need *logging*. |
| | | It's important to know what is going on inside our web application. In |
| | | development we might need to collect some output. In production, we might need |
| | | to detect problems when other people use the site. We need *logging*. |
| | | |
| | | Fortunately Pyramid uses the normal Python approach to logging. The |
| | | scaffold generated in your ``development.ini`` has a number of lines that |
| | | configure the logging for you to some reasonable defaults. You then see |
| | | messages sent by Pyramid, for example, when a new request comes in. |
| | | Fortunately Pyramid uses the normal Python approach to logging. The scaffold |
| | | generated in your ``development.ini`` has a number of lines that configure the |
| | | logging for you to some reasonable defaults. You then see messages sent by |
| | | Pyramid, for example, when a new request comes in. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Inspect the configuration setup used for logging |
| | | - Inspect the configuration setup used for logging. |
| | | |
| | | - Add logging statements to your view code |
| | | - Add logging statements to your view code. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | .. literalinclude:: logging/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. Finally let's edit ``development.ini`` configuration file |
| | | to enable logging for our Pyramid application: |
| | | #. Finally let's edit ``development.ini`` configuration file to enable logging |
| | | for our Pyramid application: |
| | | |
| | | .. literalinclude:: logging/development.ini |
| | | :language: ini |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.41 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy |
| | | in your browser. Note, both in the console and in the debug |
| | | toolbar, the message that you logged. |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser. |
| | | Note, both in the console and in the debug toolbar, the message that you |
| | | logged. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | In our configuration file ``development.ini``, our ``tutorial`` Python |
| | | package is setup as a logger and configured to log messages at a |
| | | ``DEBUG`` or higher level. When you visit http://localhost:6543 your |
| | | console will now show:: |
| | | In our configuration file ``development.ini``, our ``tutorial`` Python package |
| | | is set up as a logger and configured to log messages at a ``DEBUG`` or higher |
| | | level. When you visit http://localhost:6543, your console will now show: |
| | | |
| | | 2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view |
| | | .. code-block:: text |
| | | |
| | | 2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view |
| | | |
| | | Also, if you have configured your Pyramid application to use the |
| | | ``pyramid_debugtoolbar``, logging statements appear in one of its menus. |
| | |
| | | |
| | | Group views into a class, sharing configuration, state, and logic. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | As part of its mission to help build more ambitious web applications, |
| | | Pyramid provides many more features for views and view classes. |
| | | As part of its mission to help build more ambitious web applications, Pyramid |
| | | provides many more features for views and view classes. |
| | | |
| | | The Pyramid documentation discusses views as a Python "callable". This |
| | | callable can be a function, an object with an ``__call__``, |
| | | or a Python class. In this last case, methods on the class can be |
| | | decorated with ``@view_config`` to register the class methods with the |
| | | :term:`configurator` as a view. |
| | | The Pyramid documentation discusses views as a Python "callable". This callable |
| | | can be a function, an object with a ``__call__``, or a Python class. In this |
| | | last case, methods on the class can be decorated with ``@view_config`` to |
| | | register the class methods with the :term:`configurator` as a view. |
| | | |
| | | At first, our views were simple, free-standing functions. Many times |
| | | your views are related: different ways to look at or work on the same |
| | | data or a REST API that handles multiple operations. Grouping these |
| | | together as a :ref:`view class <class_as_view>` makes sense: |
| | | At first, our views were simple, free-standing functions. Many times your views |
| | | are related: different ways to look at or work on the same data, or a REST API |
| | | that handles multiple operations. Grouping these together as a :ref:`view class |
| | | <class_as_view>` makes sense: |
| | | |
| | | - Group views |
| | | - Group views. |
| | | |
| | | - Centralize some repetitive defaults |
| | | - Centralize some repetitive defaults. |
| | | |
| | | - Share some state and helpers |
| | | - Share some state and helpers. |
| | | |
| | | Pyramid views have :ref:`view predicates <view_configuration_parameters>` |
| | | that determine which view is matched to a request, based on factors |
| | | such as the request method, the form parameters, etc. These predicates |
| | | provide many axes of flexibility. |
| | | Pyramid views have :ref:`view predicates <view_configuration_parameters>` that |
| | | determine which view is matched to a request, based on factors such as the |
| | | request method, the form parameters, and so on. These predicates provide many |
| | | axes of flexibility. |
| | | |
| | | The following shows a simple example with four operations: |
| | | view a home page which leads to a form, save a change, |
| | | and press the delete button. |
| | | The following shows a simple example with four operations: view a home page |
| | | which leads to a form, save a change, and press the delete button. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Group related views into a view class |
| | | - Group related views into a view class. |
| | | |
| | | - Centralize configuration with class-level ``@view_defaults`` |
| | | - Centralize configuration with class-level ``@view_defaults``. |
| | | |
| | | - Dispatch one route/URL to multiple views based on request data |
| | | - Dispatch one route/URL to multiple views based on request data. |
| | | |
| | | - Share states and logic between views and templates via the view class |
| | | - Share states and logic between views and templates via the view class. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | .. literalinclude:: more_view_classes/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. Our primary view needs a template at |
| | | ``more_view_classes/tutorial/home.pt``: |
| | | #. Our primary view needs a template at ``more_view_classes/tutorial/home.pt``: |
| | | |
| | | .. literalinclude:: more_view_classes/tutorial/home.pt |
| | | :language: html |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 2 tests in 0.248s |
| | | |
| | | OK |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .. |
| | | 2 passed in 0.40 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/howdy/jane/doe in your browser. Click |
| | | the ``Save`` and ``Delete`` buttons and watch the output in the |
| | | console window. |
| | | #. Open http://localhost:6543/howdy/jane/doe in your browser. Click the |
| | | ``Save`` and ``Delete`` buttons, and watch the output in the console window. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | As you can see, the four views are logically grouped together. |
| | | Specifically: |
| | | As you can see, the four views are logically grouped together. Specifically: |
| | | |
| | | - We have a ``home`` view available at http://localhost:6543/ with |
| | | a clickable link to the ``hello`` view. |
| | | - We have a ``home`` view available at http://localhost:6543/ with a clickable |
| | | link to the ``hello`` view. |
| | | |
| | | - The second view is returned when you go to ``/howdy/jane/doe``. This |
| | | URL is |
| | | - The second view is returned when you go to ``/howdy/jane/doe``. This URL is |
| | | mapped to the ``hello`` route that we centrally set using the optional |
| | | ``@view_defaults``. |
| | | |
| | | - The third view is returned when the form is submitted with a ``POST`` |
| | | method. This rule is specified in the ``@view_config`` for that view. |
| | | - The third view is returned when the form is submitted with a ``POST`` method. |
| | | This rule is specified in the ``@view_config`` for that view. |
| | | |
| | | - The fourth view is returned when clicking on a button such |
| | | as ``<input type="submit" name="form.delete" value="Delete"/>``. |
| | | - The fourth view is returned when clicking on a button such as ``<input |
| | | type="submit" name="form.delete" value="Delete"/>``. |
| | | |
| | | In this step we show, using the following information as criteria, how to |
| | | decide which view to use: |
| | |
| | | |
| | | - Parameter information in the request (submitted form field names) |
| | | |
| | | We also centralize part of the view configuration to the class level |
| | | with ``@view_defaults``, then in one view, override that default just |
| | | for that one view. Finally, we put this commonality between views to |
| | | work in the view class by sharing: |
| | | We also centralize part of the view configuration to the class level with |
| | | ``@view_defaults``, then in one view, override that default just for that one |
| | | view. Finally, we put this commonality between views to work in the view class |
| | | by sharing: |
| | | |
| | | - State assigned in ``TutorialViews.__init__`` |
| | | |
| | | - A computed value |
| | | |
| | | These are then available both in the view methods but also in the |
| | | templates (e.g. ``${view.view_name}`` and ``${view.full_name}``. |
| | | These are then available both in the view methods and in the templates (e.g., |
| | | ``${view.view_name}`` and ``${view.full_name}``). |
| | | |
| | | As a note, we made a switch in our templates on how we generate URLs. |
| | | We previously hardcode the URLs, such as:: |
| | | As a note, we made a switch in our templates on how we generate URLs. We |
| | | previously hardcoded the URLs, such as: |
| | | |
| | | .. code-block:: html |
| | | |
| | | <a href="/howdy/jane/doe">Howdy</a> |
| | | |
| | | In ``home.pt`` we switched to:: |
| | | In ``home.pt`` we switched to: |
| | | |
| | | .. code-block:: xml |
| | | |
| | | <a href="${request.route_url('hello', first='jane', |
| | | last='doe')}">form</a> |
| | | |
| | | Pyramid has rich facilities to help generate URLs in a flexible, |
| | | non-error-prone fashion. |
| | | Pyramid has rich facilities to help generate URLs in a flexible, non-error |
| | | prone fashion. |
| | | |
| | | Extra Credit |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Why could our template do ``${view.full_name}`` and not have to do |
| | | ``${view.full_name()}``? |
| | | |
| | | #. The ``edit`` and ``delete`` views are both submitted to with |
| | | ``POST``. Why does the ``edit`` view configuration not catch the |
| | | ``POST`` used by ``delete``? |
| | | #. The ``edit`` and ``delete`` views are both receive ``POST`` requests. Why |
| | | does the ``edit`` view configuration not catch the ``POST`` used by |
| | | ``delete``? |
| | | |
| | | #. We used Python ``@property`` on ``full_name``. If we reference this |
| | | many times in a template or view code, it would re-compute this |
| | | every time. Does Pyramid provide something that will cache the initial |
| | | computation on a property? |
| | | #. We used Python ``@property`` on ``full_name``. If we reference this many |
| | | times in a template or view code, it would re-compute this every time. Does |
| | | Pyramid provide something that will cache the initial computation on a |
| | | property? |
| | | |
| | | #. Can you associate more than one route with the same view? |
| | | |
| | | #. There is also a ``request.route_path`` API. How does this differ from |
| | | #. There is also a ``request.route_path`` API. How does this differ from |
| | | ``request.route_url``? |
| | | |
| | | .. seealso:: :ref:`class_as_view`, `Weird Stuff You Can Do With |
| | |
| | | ============================================ |
| | | |
| | | Most modern Python development is done using Python packages, an approach |
| | | Pyramid puts to good use. In this step we redo "Hello World" as a |
| | | minimum Python package inside a minimum Python project. |
| | | Pyramid puts to good use. In this step we redo "Hello World" as a minimal |
| | | Python package inside a minimal Python project. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Python developers can organize a collection of modules and files into a |
| | | namespaced unit called a :ref:`package <python:tut-packages>`. If a |
| | | directory is on ``sys.path`` and has a special file named |
| | | ``__init__.py``, it is treated as a Python package. |
| | | namespaced unit called a :ref:`package <python:tut-packages>`. If a directory |
| | | is on ``sys.path`` and has a special file named ``__init__.py``, it is treated |
| | | as a Python package. |
| | | |
| | | Packages can be bundled up, made available for installation, and installed |
| | | through a toolchain oriented around a ``setup.py`` file. For this tutorial, |
| | |
| | | |
| | | - That package will be part of a *project*. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | |
| | | - Get a minimum Python "project" in place by making a ``setup.py``. |
| | | |
| | | - Install our ``tutorial`` project in development mode. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | .. literalinclude:: package/setup.py |
| | | |
| | | #. Make the new project installed for development then make a directory |
| | | for the actual code: |
| | | #. Make the new project installed for development then make a directory for the |
| | | actual code: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | |
| | | #. Open http://localhost:6543/ in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Python packages give us an organized unit of project development. |
| | | Python projects, via ``setup.py``, gives us special features when |
| | | our package is installed (in this case, in local development mode.) |
| | | Python packages give us an organized unit of project development. Python |
| | | projects, via ``setup.py``, give us special features when our package is |
| | | installed (in this case, in local development mode, also called local editable |
| | | mode as indicated by ``-e .``). |
| | | |
| | | In this step we have a Python package called ``tutorial``. We use the |
| | | same name in each step of the tutorial, to avoid unnecessary retyping. |
| | | In this step we have a Python package called ``tutorial``. We use the same name |
| | | in each step of the tutorial, to avoid unnecessary retyping. |
| | | |
| | | Above this ``tutorial`` directory we have the files that handle the |
| | | packaging of this project. At the moment, all we need is a |
| | | bare-bones ``setup.py``. |
| | | Above this ``tutorial`` directory we have the files that handle the packaging |
| | | of this project. At the moment, all we need is a bare-bones ``setup.py``. |
| | | |
| | | Everything else is the same about our application. We simply made a |
| | | Python package with a ``setup.py`` and installed it in development mode. |
| | | Everything else is the same about our application. We simply made a Python |
| | | package with a ``setup.py`` and installed it in development mode. |
| | | |
| | | Note that the way we're running the app (``python tutorial/app.py``) is a bit |
| | | of an odd duck. We would never do this unless we were writing a tutorial that |
| | | tries to capture how this stuff works a step at a time. It's generally a bad |
| | | tries to capture how this stuff works one step at a time. It's generally a bad |
| | | idea to run a Python module inside a package directly as a script. |
| | | |
| | | .. seealso:: :ref:`Python Packages <python:tut-packages>` and `Working in |
| | |
| | | ======================================= |
| | | |
| | | Web applications handle incoming requests and return outgoing responses. |
| | | Pyramid makes working with requests and responses convenient and |
| | | reliable. |
| | | Pyramid makes working with requests and responses convenient and reliable. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Learn the background on Pyramid's choices for requests and responses |
| | | - Learn the background on Pyramid's choices for requests and responses. |
| | | |
| | | - Grab data out of the request |
| | | - Grab data out of the request. |
| | | |
| | | - Change information in the response headers |
| | | - Change information in the response headers. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Developing for the web means processing web requests. As this is a |
| | | critical part of a web application, web developers need a robust, |
| | | mature set of software for web requests and returning web |
| | | responses. |
| | | Developing for the web means processing web requests. As this is a critical |
| | | part of a web application, web developers need a robust, mature set of software |
| | | for web requests and returning web responses. |
| | | |
| | | Pyramid has always fit nicely into the existing world of Python web |
| | | development (virtual environments, packaging, scaffolding, |
| | | first to embrace Python 3, etc.) For request handling, Pyramid turned |
| | | to the well-regarded :term:`WebOb` Python library for request and |
| | | response handling. In our example |
| | | above, Pyramid hands ``hello_world`` a ``request`` that is |
| | | :ref:`based on WebOb <webob_chapter>`. |
| | | Pyramid has always fit nicely into the existing world of Python web development |
| | | (virtual environments, packaging, scaffolding, first to embrace Python 3, and |
| | | so on). Pyramid turned to the well-regarded :term:`WebOb` Python library for |
| | | request and response handling. In our example above, Pyramid hands |
| | | ``hello_world`` a ``request`` that is :ref:`based on WebOb <webob_chapter>`. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | ..... |
| | | 5 passed in 0.30 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/ in your browser. You will be |
| | | redirected to http://localhost:6543/plain |
| | | #. Open http://localhost:6543/ in your browser. You will be redirected to |
| | | http://localhost:6543/plain. |
| | | |
| | | #. Open http://localhost:6543/plain?name=alice in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | In this view class we have two routes and two views, with the first |
| | | leading to the second by an HTTP redirect. Pyramid can |
| | | :ref:`generate redirects <http_redirect>` by returning a |
| | | special object from a view or raising a special exception. |
| | | In this view class, we have two routes and two views, with the first leading to |
| | | the second by an HTTP redirect. Pyramid can :ref:`generate redirects |
| | | <http_redirect>` by returning a special object from a view or raising a special |
| | | exception. |
| | | |
| | | In this Pyramid view, we get the URL being visited from ``request.url``. |
| | | Also, if you visited http://localhost:6543/plain?name=alice, |
| | | the name is included in the body of the response:: |
| | | In this Pyramid view, we get the URL being visited from ``request.url``. Also, |
| | | if you visited http://localhost:6543/plain?name=alice, the name is included in |
| | | the body of the response: |
| | | |
| | | .. code-block:: text |
| | | |
| | | URL http://localhost:6543/plain?name=alice with name: alice |
| | | |
| | | Finally, we set the response's content type and body, then return the |
| | | Response. |
| | | Finally, we set the response's content type and body, then return the response. |
| | | |
| | | We updated the unit and functional tests to prove that our code |
| | | does the redirection, but also handles sending and not sending |
| | | ``/plain?name``. |
| | | We updated the unit and functional tests to prove that our code does the |
| | | redirection, but also handles sending and not sending ``/plain?name``. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. Could we also ``raise HTTPFound(location='/plain')`` instead of |
| | | returning it? If so, what's the difference? |
| | | #. Could we also ``raise HTTPFound(location='/plain')`` instead of returning |
| | | it? If so, what's the difference? |
| | | |
| | | .. seealso:: :ref:`webob_chapter`, |
| | | :ref:`generate redirects <http_redirect>` |
| | |
| | | |
| | | Let's get our tutorial environment set up. Most of the set up work is in |
| | | standard Python development practices (install Python and make an isolated |
| | | environment.) |
| | | virtual environment.) |
| | | |
| | | .. note:: |
| | | |
| | | Pyramid encourages standard Python development practices with |
| | | packaging tools, virtual environments, logging, and so on. There |
| | | are many variations, implementations, and opinions across the Python |
| | | community. For consistency, ease of documentation maintenance, |
| | | and to minimize confusion, the Pyramid *documentation* has adopted |
| | | specific conventions. |
| | | Pyramid encourages standard Python development practices with packaging |
| | | tools, virtual environments, logging, and so on. There are many variations, |
| | | implementations, and opinions across the Python community. For consistency, |
| | | ease of documentation maintenance, and to minimize confusion, the Pyramid |
| | | *documentation* has adopted specific conventions that are consistent with the |
| | | :term:`Python Packaging Authority`. |
| | | |
| | | This *Quick Tutorial* is based on: |
| | | |
| | | * **Python 3.5**. Pyramid fully supports Python 3.3+ and Python 2.6+. This |
| | | * **Python 3.5**. Pyramid fully supports Python 3.3+ and Python 2.7+. This |
| | | tutorial uses **Python 3.5** but runs fine under Python 2.7. |
| | | |
| | | * **venv**. We believe in virtual environments. For this tutorial, we use |
| | | Python 3.5's built-in solution ``venv``. For Python 2.7, you can install |
| | | ``virtualenv``. |
| | | Python 3.5's built-in solution :term:`venv`. For Python 2.7, you can install |
| | | :term:`virtualenv`. |
| | | |
| | | * **pip**. We use ``pip`` for package management. |
| | | * **pip**. We use :term:`pip` for package management. |
| | | |
| | | * **Workspaces, projects, and packages.** Our home directory |
| | | will contain a *tutorial workspace* with our Python virtual |
| | | environment(s) and *Python projects* (a directory with packaging |
| | | information and *Python packages* of working code.) |
| | | * **Workspaces, projects, and packages.** Our home directory will contain a |
| | | *tutorial workspace* with our Python virtual environment and *Python |
| | | projects* (a directory with packaging information and *Python packages* of |
| | | working code.) |
| | | |
| | | * **Unix commands**. Commands in this tutorial use UNIX syntax and |
| | | paths. Windows users should adjust commands accordingly. |
| | | * **Unix commands**. Commands in this tutorial use UNIX syntax and paths. |
| | | Windows users should adjust commands accordingly. |
| | | |
| | | .. note:: |
| | | |
| | | Pyramid was one of the first web frameworks to fully support Python 3 in |
| | | October 2011. |
| | | |
| | | .. note:: |
| | | Windows commands use the plain old MSDOS shell. For PowerShell command |
| | | syntax, see its documentation. |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | Install Python 3 |
| | | ---------------- |
| | | |
| | | Windows and Mac OS X users can download and run an installer. |
| | | See the detailed recommendation for your operating system described under |
| | | :ref:`installing_chapter`. |
| | | |
| | | Download the latest standard Python 3 release (not development release) from |
| | | `python.org <https://www.python.org/downloads/>`_. |
| | | |
| | | Windows users should also install the `Python for Windows extensions |
| | | <http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read the |
| | | ``README.txt`` file at the end of the list of builds, and follow its |
| | | directions. Make sure you get the proper 32- or 64-bit build and Python |
| | | version. |
| | | |
| | | Linux users can either use their package manager to install Python 3 |
| | | or may `build Python 3 from source |
| | | <http://pyramid.readthedocs.org/en/master/narr/install.html#package-manager- |
| | | method>`_. |
| | | |
| | | .. seealso:: See also :ref:`For Mac OS X Users <for-mac-os-x-users>`, :ref:`If |
| | | You Don't Yet Have a Python Interpreter (UNIX) |
| | | <if-you-don-t-yet-have-a-python-interpreter-unix>`, and :ref:`If You Don't |
| | | Yet Have a Python Interpreter (Windows) |
| | | <if-you-don-t-yet-have-a-python-interpreter-windows>`. |
| | | - :ref:`for-mac-os-x-users` |
| | | - :ref:`if-you-don-t-yet-have-a-python-interpreter-unix` |
| | | - :ref:`if-you-don-t-yet-have-a-python-interpreter-windows` |
| | | |
| | | |
| | | .. _create-a-project-directory-structure: |
| | |
| | | Create a project directory structure |
| | | ------------------------------------ |
| | | |
| | | We will arrive at a directory structure of |
| | | ``workspace->project->package``, with our workspace named |
| | | ``quick_tutorial``. The following tree diagram shows how this will be |
| | | structured and where our virtual environment will reside as we proceed through |
| | | the tutorial: |
| | | We will arrive at a directory structure of ``workspace -> project -> package``, |
| | | where our workspace is named ``quick_tutorial``. The following tree diagram |
| | | shows how this will be structured, and where our :term:`virtual environment` |
| | | will reside as we proceed through the tutorial: |
| | | |
| | | .. code-block:: text |
| | | |
| | |
| | | |
| | | For Windows: |
| | | |
| | | .. code-block:: ps1con |
| | | .. code-block:: doscon |
| | | |
| | | # Windows |
| | | c:\> cd \ |
| | | c:\> mkdir projects\quick_tutorial |
| | | c:\> cd projects\quick_tutorial |
| | | |
| | | In the above figure, your user home directory is represented by ``~``. In |
| | | your home directory, all of your projects are in the ``projects`` directory. |
| | | This is a general convention not specific to Pyramid that many developers use. |
| | | Windows users will do well to use ``c:\`` as the location for ``projects`` in |
| | | order to avoid spaces in any of the path names. |
| | | In the above figure, your user home directory is represented by ``~``. In your |
| | | home directory, all of your projects are in the ``projects`` directory. This is |
| | | a general convention not specific to Pyramid that many developers use. Windows |
| | | users will do well to use ``c:\`` as the location for ``projects`` in order to |
| | | avoid spaces in any of the path names. |
| | | |
| | | Next within ``projects`` is your workspace directory, here named |
| | | ``quick_tutorial``. A workspace is a common term used by integrated |
| | | development environments (IDE) like PyCharm and PyDev that stores |
| | | isolated Python environments (virtual environments) and specific project files |
| | | and repositories. |
| | | development environments (IDE), like PyCharm and PyDev, where virtual |
| | | environments, specific project files, and repositories are stored. |
| | | |
| | | |
| | | .. _set-an-environment-variable: |
| | | |
| | | Set an Environment Variable |
| | | Set an environment variable |
| | | --------------------------- |
| | | |
| | | This tutorial will refer frequently to the location of the virtual |
| | | environment. We set an environment variable to save typing later. |
| | | This tutorial will refer frequently to the location of the :term:`virtual |
| | | environment`. We set an environment variable to save typing later. |
| | | |
| | | .. code-block:: bash |
| | | |
| | | # Mac and Linux |
| | | $ export VENV=~/projects/quick_tutorial/env |
| | | |
| | | .. code-block:: ps1con |
| | | .. code-block:: doscon |
| | | |
| | | # Windows |
| | | # TODO: This command does not work |
| | | c:\> set VENV=c:\projects\quick_tutorial\env |
| | | |
| | | |
| | | .. _create-a-virtual-environment: |
| | | |
| | | Create a Virtual Environment |
| | | Create a virtual environment |
| | | ---------------------------- |
| | | |
| | | ``venv`` is a tool to create isolated Python 3 environments, each with its own |
| | |
| | | # Mac and Linux |
| | | $ python3 -m venv $VENV |
| | | |
| | | .. code-block:: ps1con |
| | | .. code-block:: doscon |
| | | |
| | | # Windows |
| | | c:\> c:\Python35\python3 -m venv %VENV% |
| | |
| | | 2's `virtualenv <https://virtualenv.pypa.io/en/latest/>`_ package. |
| | | |
| | | |
| | | Update packaging tools in the virtual environment |
| | | ------------------------------------------------- |
| | | |
| | | It's always a good idea to update to the very latest version of packaging tools |
| | | because the installed Python bundles only the version that was available at the |
| | | time of its release. |
| | | |
| | | .. code-block:: bash |
| | | |
| | | # Mac and Linux |
| | | $VENV/bin/pip install --upgrade pip setuptools |
| | | |
| | | .. code-block:: doscon |
| | | |
| | | # Windows |
| | | c:\> %VENV%\Scripts\pip install --upgrade pip setuptools |
| | | |
| | | |
| | | .. _install-pyramid: |
| | | |
| | | Install Pyramid |
| | | --------------- |
| | | |
| | | We have our Python standard prerequisites out of the way. The Pyramid |
| | | part is pretty easy: |
| | | part is pretty easy. |
| | | |
| | | .. parsed-literal:: |
| | | |
| | |
| | | |
| | | Our Python virtual environment now has the Pyramid software available. |
| | | |
| | | You can optionally install some of the extra Python packages used |
| | | during this tutorial: |
| | | You can optionally install some of the extra Python packages used in this |
| | | tutorial. |
| | | |
| | | .. code-block:: bash |
| | | |
| | | # Mac and Linux |
| | | $ $VENV/bin/pip install nose webtest deform sqlalchemy \ |
| | | pyramid_chameleon pyramid_debugtoolbar waitress \ |
| | | pyramid_tm zope.sqlalchemy |
| | | $ $VENV/bin/pip install webtest pytest pytest-cov deform sqlalchemy \ |
| | | pyramid_chameleon pyramid_debugtoolbar pyramid_jinja2 waitress \ |
| | | pyramid_tm zope.sqlalchemy |
| | | |
| | | .. code-block:: ps1con |
| | | .. code-block:: doscon |
| | | |
| | | # Windows |
| | | c:\> %VENV%\Scripts\pip install nose webtest deform sqlalchemy pyramid_chameleon pyramid_debugtoolbar waitress pyramid_tm zope.sqlalchemy |
| | | |
| | | c:\> %VENV%\Scripts\pip install webtest deform sqlalchemy pyramid_chameleon pyramid_debugtoolbar pyramid_jinja2 waitress pyramid_tm zope.sqlalchemy |
| | |
| | | 11: Dispatching URLs To Views With Routing |
| | | ========================================== |
| | | |
| | | Routing matches incoming URL patterns to view code. Pyramid's routing |
| | | has a number of useful features. |
| | | Routing matches incoming URL patterns to view code. Pyramid's routing has a |
| | | number of useful features. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Writing web applications usually means sophisticated URL design. We |
| | | just saw some Pyramid machinery for requests and views. Let's look at |
| | | features that help in routing. |
| | | Writing web applications usually means sophisticated URL design. We just saw |
| | | some Pyramid machinery for requests and views. Let's look at features that help |
| | | in routing. |
| | | |
| | | Previously we saw the basics of routing URLs to views in Pyramid. |
| | | |
| | | - Your project's "setup" code registers a route name to be used when |
| | | matching part of the URL |
| | | - Your project's "setup" code registers a route name to be used when matching |
| | | part of the URL |
| | | |
| | | - Elsewhere, a view is configured to be called for that route name |
| | | - Elsewhere a view is configured to be called for that route name. |
| | | |
| | | .. note:: |
| | | |
| | |
| | | <http://static.repoze.org/casts/videotags.html>`_ if you're interested in |
| | | doing so. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Define a route that extracts part of the URL into a Python dictionary |
| | | - Define a route that extracts part of the URL into a Python dictionary. |
| | | |
| | | - Use that dictionary data in a view |
| | | - Use that dictionary data in a view. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q |
| | | .. |
| | | 2 passed in 0.39 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/howdy/amy/smith in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | |
| | | |
| | | config.add_route('hello', '/howdy/{first}/{last}') |
| | | |
| | | With this we tell the :term:`configurator` that our URL has |
| | | a "replacement pattern". With this, URLs such as ``/howdy/amy/smith`` |
| | | will assign ``amy`` to ``first`` and ``smith`` to ``last``. We can then |
| | | use this data in our view: |
| | | With this we tell the :term:`configurator` that our URL has a "replacement |
| | | pattern". With this, URLs such as ``/howdy/amy/smith`` will assign ``amy`` to |
| | | ``first`` and ``smith`` to ``last``. We can then use this data in our view: |
| | | |
| | | .. code-block:: python |
| | | |
| | | self.request.matchdict['first'] |
| | | self.request.matchdict['last'] |
| | | |
| | | ``request.matchdict`` contains values from the URL that match the |
| | | "replacement patterns" (the curly braces) in the route declaration. |
| | | This information can then be used anywhere in Pyramid that has access |
| | | to the request. |
| | | ``request.matchdict`` contains values from the URL that match the "replacement |
| | | patterns" (the curly braces) in the route declaration. This information can |
| | | then be used anywhere in Pyramid that has access to the request. |
| | | |
| | | Extra Credit |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. What happens if you to go the URL |
| | | http://localhost:6543/howdy? Is this the result that you |
| | | expected? |
| | | #. What happens if you to go the URL http://localhost:6543/howdy? Is this the |
| | | result that you expected? |
| | | |
| | | .. seealso:: `Weird Stuff You Can Do With URL |
| | | Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_ |
| | | .. seealso:: `Weird Stuff You Can Do With URL Dispatch |
| | | <http://www.plope.com/weird_pyramid_urldispatch>`_ |
| | |
| | | Prelude: Quick Project Startup with Scaffolds |
| | | ============================================= |
| | | |
| | | To ease the process of getting started, Pyramid provides *scaffolds* |
| | | that generate sample projects from templates in Pyramid and Pyramid |
| | | add-ons. |
| | | To ease the process of getting started, Pyramid provides *scaffolds* that |
| | | generate sample projects from templates in Pyramid and Pyramid add-ons. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | We're going to cover a lot in this tutorial, focusing on one topic at a |
| | | time and writing everything from scratch. As a warm up, though, |
| | | it sure would be nice to see some pixels on a screen. |
| | | We're going to cover a lot in this tutorial, focusing on one topic at a time |
| | | and writing everything from scratch. As a warm up, though, it sure would be |
| | | nice to see some pixels on a screen. |
| | | |
| | | Like other web development frameworks, Pyramid provides a number of |
| | | "scaffolds" that generate working Python, template, and CSS code for |
| | | sample applications. In this step we'll use a built-in scaffold to let |
| | | us preview a Pyramid application, before starting from scratch on Step 1. |
| | | Like other web development frameworks, Pyramid provides a number of "scaffolds" |
| | | that generate working Python, template, and CSS code for sample applications. |
| | | In this step we'll use a built-in scaffold to let us preview a Pyramid |
| | | application, before starting from scratch on Step 1. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Use Pyramid's ``pcreate`` command to list scaffolds and make a new |
| | | project |
| | | - Use Pyramid's ``pcreate`` command to list scaffolds and make a new project. |
| | | |
| | | - Start up a Pyramid application and visit it in a web browser |
| | | - Start up a Pyramid application and visit it in a web browser. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | $ cd scaffolds |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Start up the application by pointing Pyramid's ``pserve`` command at |
| | | the project's (generated) configuration file: |
| | | #. Start up the application by pointing Pyramid's ``pserve`` command at the |
| | | project's (generated) configuration file: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | Analysis |
| | | ======== |
| | | |
| | | Rather than starting from scratch, ``pcreate`` can make getting a |
| | | Python project containing a Pyramid application a quick matter. |
| | | Pyramid ships with a few scaffolds. But installing a Pyramid add-on can |
| | | give you new scaffolds from that add-on. |
| | | Rather than starting from scratch, ``pcreate`` can make getting a Python |
| | | project containing a Pyramid application a quick matter. Pyramid ships with a |
| | | few scaffolds. But installing a Pyramid add-on can give you new scaffolds from |
| | | that add-on. |
| | | |
| | | ``pserve`` is Pyramid's application runner, separating operational |
| | | details from your code. When you install Pyramid, a small command |
| | | program called ``pserve`` is written to your ``bin`` directory. This |
| | | program is an executable Python module. It is passed a configuration |
| | | file (in this case, ``development.ini``.) |
| | | ``pserve`` is Pyramid's application runner, separating operational details from |
| | | your code. When you install Pyramid, a small command program called ``pserve`` |
| | | is written to your ``bin`` directory. This program is an executable Python |
| | | module. It is passed a configuration file (in this case, ``development.ini``). |
| | |
| | | |
| | | Store and retrieve non-permanent data in Pyramid sessions. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | When people use your web application, they frequently perform a task |
| | | that requires semi-permanent data to be saved. For example, a shopping |
| | | cart. This is called a :term:`session`. |
| | | When people use your web application, they frequently perform a task that |
| | | requires semi-permanent data to be saved. For example, a shopping cart. This is |
| | | called a :term:`session`. |
| | | |
| | | Pyramid has basic built-in support for sessions. Third party packages such as |
| | | ``pyramid_redis_sessions`` provide richer session support. Or you can create |
| | | your own custom sessioning engine. Let's take a look at the |
| | | :doc:`built-in sessioning support <../narr/sessions>`. |
| | | `pyramid_redis_sessions |
| | | <https://github.com/ericrasmussen/pyramid_redis_sessions>`_ provide richer |
| | | session support. Or you can create your own custom sessioning engine. Let's |
| | | take a look at the :doc:`built-in sessioning support <../narr/sessions>`. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Make a session factory using a built-in, simple Pyramid sessioning |
| | | system |
| | | - Make a session factory using a built-in, simple Pyramid sessioning system. |
| | | |
| | | - Change our code to use a session |
| | | - Change our code to use a session. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | $ cd ..; cp -r view_classes sessions; cd sessions |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Our ``sessions/tutorial/__init__.py`` needs a choice of session |
| | | factory to get registered with the :term:`configurator`: |
| | | #. Our ``sessions/tutorial/__init__.py`` needs a choice of session factory to |
| | | get registered with the :term:`configurator`: |
| | | |
| | | .. literalinclude:: sessions/tutorial/__init__.py |
| | | :linenos: |
| | | |
| | | #. Our views in ``sessions/tutorial/views.py`` can now use |
| | | ``request.session``: |
| | | #. Our views in ``sessions/tutorial/views.py`` can now use ``request.session``: |
| | | |
| | | .. literalinclude:: sessions/tutorial/views.py |
| | | :linenos: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.42 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy |
| | | in your browser. As you reload and switch between those URLs, note |
| | | that the counter increases and is *not* specific to the URL. |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser. |
| | | As you reload and switch between those URLs, note that the counter increases |
| | | and is *not* specific to the URL. |
| | | |
| | | #. Restart the application and revisit the page. Note that counter |
| | | still increases from where it left off. |
| | | #. Restart the application and revisit the page. Note that counter still |
| | | increases from where it left off. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Pyramid's :term:`request` object now has a ``session`` attribute |
| | | that we can use in our view code. It acts like a dictionary. |
| | | Pyramid's :term:`request` object now has a ``session`` attribute that we can |
| | | use in our view code. It acts like a dictionary. |
| | | |
| | | Since all the views are using the same counter, we made the counter a |
| | | Python property at the view class level. With this, each reload will |
| | | increase the counter displayed in our template. |
| | | Since all the views are using the same counter, we made the counter a Python |
| | | property at the view class level. With this, each reload will increase the |
| | | counter displayed in our template. |
| | | |
| | | In web development, "flash messages" are notes for the user that need |
| | | to appear on a screen after a future web request. For example, |
| | | when you add an item using a form ``POST``, the site usually issues a |
| | | second HTTP Redirect web request to view the new item. You might want a |
| | | message to appear after that second web request saying "Your item was |
| | | added." You can't just return it in the web response for the POST, |
| | | as it will be tossed out during the second web request. |
| | | In web development, "flash messages" are notes for the user that need to appear |
| | | on a screen after a future web request. For example, when you add an item using |
| | | a form ``POST``, the site usually issues a second HTTP Redirect web request to |
| | | view the new item. You might want a message to appear after that second web |
| | | request saying "Your item was added." You can't just return it in the web |
| | | response for the POST, as it will be tossed out during the second web request. |
| | | |
| | | Flash messages are a technique where messages can be stored between |
| | | requests, using sessions, then removed when they finally get displayed. |
| | | Flash messages are a technique where messages can be stored between requests, |
| | | using sessions, then removed when they finally get displayed. |
| | | |
| | | .. seealso:: |
| | | :ref:`sessions_chapter`, |
| | |
| | | 13: CSS/JS/Images Files With Static Assets |
| | | ========================================== |
| | | |
| | | Of course the Web is more than just markup. You need static assets: |
| | | CSS, JS, and images. Let's point our web app at a directory where |
| | | Pyramid will serve some static assets. |
| | | Of course the Web is more than just markup. You need static assets: CSS, JS, |
| | | and images. Let's point our web app at a directory where Pyramid will serve |
| | | some static assets. |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Publish a directory of static assets at a URL |
| | | - Publish a directory of static assets at a URL. |
| | | |
| | | - Use Pyramid to help generate URLs to files in that directory |
| | | - Use Pyramid to help generate URLs to files in that directory. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | |
| | | .. literalinclude:: static_assets/tutorial/home.pt |
| | | :language: html |
| | | |
| | | #. Add a CSS file at |
| | | ``static_assets/tutorial/static/app.css``: |
| | | #. Add a CSS file at ``static_assets/tutorial/static/app.css``: |
| | | |
| | | .. literalinclude:: static_assets/tutorial/static/app.css |
| | | :language: css |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/$VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.50 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | #. Open http://localhost:6543/ in your browser and note the new font. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | We changed our WSGI application to map requests under |
| | | http://localhost:6543/static/ to files and directories inside a |
| | | ``static`` directory inside our ``tutorial`` package. This directory |
| | | contained ``app.css``. |
| | | http://localhost:6543/static/ to files and directories inside a ``static`` |
| | | directory inside our ``tutorial`` package. This directory contained |
| | | ``app.css``. |
| | | |
| | | We linked to the CSS in our template. We could have hard-coded this |
| | | link to ``/static/app.css``. But what if the site is later moved under |
| | | ``/somesite/static/``? Or perhaps the web developer changes the |
| | | arrangement on disk? Pyramid gives a helper that provides flexibility |
| | | on URL generation: |
| | | We linked to the CSS in our template. We could have hard-coded this link to |
| | | ``/static/app.css``. But what if the site is later moved under |
| | | ``/somesite/static/``? Or perhaps the web developer changes the arrangement on |
| | | disk? Pyramid gives a helper that provides flexibility on URL generation: |
| | | |
| | | .. code-block:: html |
| | | |
| | | ${request.static_url('tutorial:static/app.css')} |
| | | |
| | | This matches the ``path='tutorial:static'`` in our |
| | | ``config.add_static_view`` registration. By using ``request.static_url`` |
| | | to generate the full URL to the static assets, you both ensure you stay |
| | | in sync with the configuration and gain refactoring flexibility later. |
| | | This matches the ``path='tutorial:static'`` in our ``config.add_static_view`` |
| | | registration. By using ``request.static_url`` to generate the full URL to the |
| | | static assets, you both ensure you stay in sync with the configuration and gain |
| | | refactoring flexibility later. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. There is also a ``request.static_path`` API. How does this differ from |
| | |
| | | 08: HTML Generation With Templating |
| | | =================================== |
| | | |
| | | Most web frameworks don't embed HTML in programming code. Instead, |
| | | they pass data into a templating system. In this step we look at the |
| | | basics of using HTML templates in Pyramid. |
| | | Most web frameworks don't embed HTML in programming code. Instead, they pass |
| | | data into a templating system. In this step we look at the basics of using HTML |
| | | templates in Pyramid. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | Ouch. We have been making our own ``Response`` and filling the response |
| | | body with HTML. You usually won't embed an HTML string directly in |
| | | Python, but instead, will use a templating language. |
| | | Ouch. We have been making our own ``Response`` and filling the response body |
| | | with HTML. You usually won't embed an HTML string directly in Python, but |
| | | instead will use a templating language. |
| | | |
| | | Pyramid doesn't mandate a particular database system, form library, |
| | | etc. It encourages replaceability. This applies equally to templating, |
| | | which is fortunate: developers have strong views about template |
| | | languages. As of Pyramid 1.5a2, Pyramid doesn't even bundle a template |
| | | language! |
| | | Pyramid doesn't mandate a particular database system, form library, and so on. |
| | | It encourages replaceability. This applies equally to templating, which is |
| | | fortunate: developers have strong views about template languages. As of |
| | | Pyramid 1.5a2, Pyramid doesn't even bundle a template language! |
| | | |
| | | It does, however, have strong ties to Jinja2, Mako, and Chameleon. In |
| | | this step we see how to add ``pyramid_chameleon`` to your project, |
| | | then change your views to use templating. |
| | | It does, however, have strong ties to Jinja2, Mako, and Chameleon. In this step |
| | | we see how to add `pyramid_chameleon |
| | | <https://github.com/Pylons/pyramid_chameleon>`_ to your project, then change |
| | | your views to use templating. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Enable the ``pyramid_chameleon`` Pyramid add-on |
| | | - Enable the ``pyramid_chameleon`` Pyramid add-on. |
| | | |
| | | - Generate HTML from template files |
| | | - Generate HTML from template files. |
| | | |
| | | - Connect the templates as "renderers" for view code |
| | | - Connect the templates as "renderers" for view code. |
| | | |
| | | - Change the view code to simply return data |
| | | - Change the view code to simply return data. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. Let's begin by using the previous package as a starting point for a |
| | | new project: |
| | | #. Let's begin by using the previous package as a starting point for a new |
| | | project: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ cd ..; cp -r views templating; cd templating |
| | | |
| | | #. This step depends on ``pyramid_chameleon``, so add it as a dependency |
| | | in ``templating/setup.py``: |
| | | #. This step depends on ``pyramid_chameleon``, so add it as a dependency in |
| | | ``templating/setup.py``: |
| | | |
| | | .. literalinclude:: templating/setup.py |
| | | :linenos: |
| | |
| | | |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. We need to connect ``pyramid_chameleon`` as a renderer by making a |
| | | call in the setup of ``templating/tutorial/__init__.py``: |
| | | #. We need to connect ``pyramid_chameleon`` as a renderer by making a call in |
| | | the setup of ``templating/tutorial/__init__.py``: |
| | | |
| | | .. literalinclude:: templating/tutorial/__init__.py |
| | | :linenos: |
| | |
| | | .. literalinclude:: templating/tutorial/home.pt |
| | | :language: html |
| | | |
| | | #. For convenience, change ``templating/development.ini`` to reload |
| | | templates automatically with ``pyramid.reload_templates``: |
| | | #. For convenience, change ``templating/development.ini`` to reload templates |
| | | automatically with ``pyramid.reload_templates``: |
| | | |
| | | .. literalinclude:: templating/development.ini |
| | | :language: ini |
| | | |
| | | #. Our unit tests in ``templating/tutorial/tests.py`` can focus on |
| | | data: |
| | | #. Our unit tests in ``templating/tutorial/tests.py`` can focus on data: |
| | | |
| | | .. literalinclude:: templating/tutorial/tests.py |
| | | :linenos: |
| | |
| | | |
| | | .. code-block:: bash |
| | | |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 4 tests in 0.141s |
| | | |
| | | OK |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.46 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy |
| | | in your browser. |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Ahh, that looks better. We have a view that is focused on Python code. |
| | | Our ``@view_config`` decorator specifies a :term:`renderer` that points |
| | | to our template file. Our view then simply returns data which is then |
| | | supplied to our template. Note that we used the same template for both |
| | | views. |
| | | Ahh, that looks better. We have a view that is focused on Python code. Our |
| | | ``@view_config`` decorator specifies a :term:`renderer` that points to our |
| | | template file. Our view then simply returns data which is then supplied to our |
| | | template. Note that we used the same template for both views. |
| | | |
| | | Note the effect on testing. We can focus on having a data-oriented |
| | | contract with our view code. |
| | | Note the effect on testing. We can focus on having a data-oriented contract |
| | | with our view code. |
| | | |
| | | .. seealso:: :ref:`templates_chapter`, :ref:`debugging_templates`, and |
| | | :ref:`available_template_system_bindings`. |
| | |
| | | Tutorial Approach |
| | | ================= |
| | | |
| | | This tutorial uses conventions to keep the introduction focused and |
| | | concise. Details, references, and deeper discussions are mentioned in |
| | | "See also" notes. |
| | | This tutorial uses conventions to keep the introduction focused and concise. |
| | | Details, references, and deeper discussions are mentioned in "See also" notes. |
| | | |
| | | .. seealso:: This is an example "See also" note. |
| | | |
| | | This "Getting Started" tutorial is broken into independent steps, |
| | | starting with the smallest possible "single file WSGI app" example. |
| | | Each of these steps introduce a topic and a very small set of concepts |
| | | via working code. The steps each correspond to a directory in this |
| | | repo, where each step/topic/directory is a Python package. |
| | | This "Getting Started" tutorial is broken into independent steps, starting with |
| | | the smallest possible "single file WSGI app" example. Each of these steps |
| | | introduce a topic and a very small set of concepts via working code. The steps |
| | | each correspond to a directory in this repo, where each step/topic/directory is |
| | | a Python package. |
| | | |
| | | To successfully run each step:: |
| | | To successfully run each step: |
| | | |
| | | $ cd request_response |
| | | $ $VENV/bin/pip install -e . |
| | | .. code-block:: bash |
| | | |
| | | ...and repeat for each step you would like to work on. In most cases we |
| | | will start with the results of an earlier step. |
| | | $ cd request_response |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | Directory Tree |
| | | ...and repeat for each step you would like to work on. In most cases we will |
| | | start with the results of an earlier step. |
| | | |
| | | Directory tree |
| | | ============== |
| | | |
| | | As we develop our tutorial our directory tree will resemble the |
| | | structure below:: |
| | | As we develop our tutorial, our directory tree will resemble the structure |
| | | below: |
| | | |
| | | quicktutorial/ |
| | | request_response/ |
| | | development.ini |
| | | setup.py |
| | | tutorial/ |
| | | __init__.py |
| | | home.pt |
| | | tests.py |
| | | views.py |
| | | .. code-block:: text |
| | | |
| | | Each of the first-level directories (e.g. ``request_response``) is a |
| | | *Python project* (except, as noted, the ``hello_world`` step.) The |
| | | ``tutorial`` directory is a *Python package*. At the end of each step, |
| | | we copy a previous directory into a new directory to use as a starting |
| | | point. |
| | | quick_tutorial |
| | | ├── env |
| | | └── request_response |
| | | ├── tutorial |
| | | │ ├── __init__.py |
| | | │ ├── tests.py |
| | | │ └── views.py |
| | | ├── development.ini |
| | | └── setup.py |
| | | |
| | | Each of the first-level directories (e.g., ``request_response``) is a *Python |
| | | project* (except as noted for the ``hello_world`` step). The ``tutorial`` |
| | | directory is a *Python package*. At the end of each step, we copy a previous |
| | | directory into a new directory to use as a starting point. |
| | |
| | | .. _qtut_unit_testing: |
| | | |
| | | =========================== |
| | | 05: Unit Tests and ``nose`` |
| | | =========================== |
| | | ============================= |
| | | 05: Unit Tests and ``pytest`` |
| | | ============================= |
| | | |
| | | Provide unit testing for our project's Python code. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | As the mantra says, "Untested code is broken code." The Python |
| | | community has had a long culture of writing test scripts which ensure |
| | | that your code works correctly as you write it and maintain it in the |
| | | future. Pyramid has always had a deep commitment to testing, |
| | | with 100% test coverage from the earliest pre-releases. |
| | | As the mantra says, "Untested code is broken code." The Python community has |
| | | had a long culture of writing test scripts which ensure that your code works |
| | | correctly as you write it and maintain it in the future. Pyramid has always had |
| | | a deep commitment to testing, with 100% test coverage from the earliest |
| | | pre-releases. |
| | | |
| | | Python includes a |
| | | :ref:`unit testing framework <python:unittest-minimal-example>` in its |
| | | standard library. Over the years a number of Python projects, such as |
| | | `nose <https://pypi.python.org/pypi/nose/>`_, have extended this |
| | | framework with alternative test runners that provide more convenience |
| | | and functionality. The Pyramid developers use ``nose``, which we'll thus |
| | | use in this tutorial. |
| | | Python includes a :ref:`unit testing framework |
| | | <python:unittest-minimal-example>` in its standard library. Over the years a |
| | | number of Python projects, such as :ref:`pytest <pytest:features>`, have |
| | | extended this framework with alternative test runners that provide more |
| | | convenience and functionality. The Pyramid developers use ``pytest``, which |
| | | we'll use in this tutorial. |
| | | |
| | | Don't worry, this tutorial won't be pedantic about "test-driven |
| | | development" (TDD). We'll do just enough to ensure that, in each step, |
| | | we haven't majorly broken the code. As you're writing your code you |
| | | might find this more convenient than changing to your browser |
| | | constantly and clicking reload. |
| | | Don't worry, this tutorial won't be pedantic about "test-driven development" |
| | | (TDD). We'll do just enough to ensure that, in each step, we haven't majorly |
| | | broken the code. As you're writing your code, you might find this more |
| | | convenient than changing to your browser constantly and clicking reload. |
| | | |
| | | We'll also leave discussion of |
| | | `coverage <https://pypi.python.org/pypi/coverage>`_ for another section. |
| | | We'll also leave discussion of `pytest-cov |
| | | <http://pytest-cov.readthedocs.org/en/latest/>`_ for another section. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Write unit tests that ensure the quality of our code |
| | | - Write unit tests that ensure the quality of our code. |
| | | |
| | | - Install a Python package (``nose``) which helps in our testing |
| | | - Install a Python package (``pytest``) which helps in our testing. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. First we copy the results of the previous step, as well as install |
| | | the ``nose`` package: |
| | | #. First we copy the results of the previous step, as well as install the |
| | | ``pytest`` package: |
| | | |
| | | .. code-block:: bash |
| | | |
| | | $ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing |
| | | $ $VENV/bin/pip install -e . |
| | | $ $VENV/bin/pip install nose |
| | | $ $VENV/bin/pip install pytest |
| | | |
| | | #. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``: |
| | | |
| | |
| | | .. code-block:: bash |
| | | |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 1 test in 0.141s |
| | | 1 passed in 0.14 seconds |
| | | |
| | | OK |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | Our ``tests.py`` imports the Python standard unit testing framework. To |
| | | make writing Pyramid-oriented tests more convenient, Pyramid supplies |
| | | some ``pyramid.testing`` helpers which we use in the test setup and |
| | | teardown. Our one test imports the view, makes a dummy request, and sees |
| | | if the view returns what we expected. |
| | | Our ``tests.py`` imports the Python standard unit testing framework. To make |
| | | writing Pyramid-oriented tests more convenient, Pyramid supplies some |
| | | ``pyramid.testing`` helpers which we use in the test setup and teardown. Our |
| | | one test imports the view, makes a dummy request, and sees if the view returns |
| | | what we expect. |
| | | |
| | | The ``tests.TutorialViewTests.test_hello_world`` test is a small |
| | | example of a unit test. First, we import the view inside each test. Why |
| | | not import at the top, like in normal Python code? Because imports can |
| | | cause effects that break a test. We'd like our tests to be in *units*, |
| | | hence the name *unit* testing. Each test should isolate itself to the |
| | | correct degree. |
| | | The ``tests.TutorialViewTests.test_hello_world`` test is a small example of a |
| | | unit test. First, we import the view inside each test. Why not import at the |
| | | top, like in normal Python code? Because imports can cause effects that break a |
| | | test. We'd like our tests to be in *units*, hence the name *unit* testing. Each |
| | | test should isolate itself to the correct degree. |
| | | |
| | | Our test then makes a fake incoming web request, then calls our Pyramid |
| | | view. We test the HTTP status code on the response to make sure it |
| | | matches our expectations. |
| | | Our test then makes a fake incoming web request, then calls our Pyramid view. |
| | | We test the HTTP status code on the response to make sure it matches our |
| | | expectations. |
| | | |
| | | Note that our use of ``pyramid.testing.setUp()`` and |
| | | ``pyramid.testing.tearDown()`` aren't actually necessary here; they are only |
| | | necessary when your test needs to make use of the ``config`` object (it's a |
| | | Configurator) to add stuff to the configuration state before calling the view. |
| | | |
| | | |
| | | Extra Credit |
| | | ============ |
| | | |
| | | #. Change the test to assert that the response status code should be |
| | | ``404`` (meaning, not found.) Run ``nosetests`` again. Read the |
| | | error report and see if you can decipher what it is telling you. |
| | | #. Change the test to assert that the response status code should be ``404`` |
| | | (meaning, not found). Run ``py.test`` again. Read the error report and see |
| | | if you can decipher what it is telling you. |
| | | |
| | | #. As a more realistic example, put the ``tests.py`` back as you found |
| | | it and put an error in your view, such as a reference to a |
| | | non-existing variable. Run the tests and see how this is more |
| | | convenient than reloading your browser and going back to your code. |
| | | #. As a more realistic example, put the ``tests.py`` back as you found it, and |
| | | put an error in your view, such as a reference to a non-existing variable. |
| | | Run the tests and see how this is more convenient than reloading your |
| | | browser and going back to your code. |
| | | |
| | | #. Finally, for the most realistic test, read about Pyramid ``Response`` |
| | | objects and see how to change the response code. Run the tests and |
| | | see how testing confirms the "contract" that your code claims to |
| | | support. |
| | | objects and see how to change the response code. Run the tests and see how |
| | | testing confirms the "contract" that your code claims to support. |
| | | |
| | | #. How could we add a unit test assertion to test the HTML value of the |
| | | response body? |
| | |
| | | 09: Organizing Views With View Classes |
| | | ====================================== |
| | | |
| | | Change our view functions to be methods on a view class, |
| | | then move some declarations to the class level. |
| | | Change our view functions to be methods on a view class, then move some |
| | | declarations to the class level. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | |
| | | on the same data, or be a REST API that handles multiple operations. Grouping |
| | | these views together as a :ref:`view class <class_as_view>` makes sense: |
| | | |
| | | - Group views |
| | | - Group views. |
| | | |
| | | - Centralize some repetitive defaults |
| | | - Centralize some repetitive defaults. |
| | | |
| | | - Share some state and helpers |
| | | - Share some state and helpers. |
| | | |
| | | In this step we just do the absolute minimum to convert the existing |
| | | views to a view class. In a later tutorial step we'll examine view |
| | | classes in depth. |
| | | In this step we just do the absolute minimum to convert the existing views to a |
| | | view class. In a later tutorial step, we'll examine view classes in depth. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Group related views into a view class |
| | | - Group related views into a view class. |
| | | |
| | | - Centralize configuration with class-level ``@view_defaults`` |
| | | - Centralize configuration with class-level ``@view_defaults``. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | |
| | | #. First we copy the results of the previous step: |
| | | |
| | |
| | | $ cd ..; cp -r templating view_classes; cd view_classes |
| | | $ $VENV/bin/pip install -e . |
| | | |
| | | #. Our ``view_classes/tutorial/views.py`` now has a view class with |
| | | our two views: |
| | | #. Our ``view_classes/tutorial/views.py`` now has a view class with our two |
| | | views: |
| | | |
| | | .. literalinclude:: view_classes/tutorial/views.py |
| | | :linenos: |
| | | |
| | | #. Our unit tests in ``view_classes/tutorial/tests.py`` don't run, |
| | | so let's modify them to import the view class and make an instance |
| | | before getting a response: |
| | | #. Our unit tests in ``view_classes/tutorial/tests.py`` don't run, so let's |
| | | modify them to import the view class, and make an instance before getting a |
| | | response: |
| | | |
| | | .. literalinclude:: view_classes/tutorial/tests.py |
| | | :linenos: |
| | |
| | | .. code-block:: bash |
| | | |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 4 tests in 0.141s |
| | | |
| | | OK |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.34 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | |
| | | $ $VENV/bin/pserve development.ini --reload |
| | | |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy |
| | | in your browser. |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | To ease the transition to view classes, we didn't introduce any new |
| | | functionality. We simply changed the view functions to methods on a |
| | | view class, then updated the tests. |
| | | functionality. We simply changed the view functions to methods on a view class, |
| | | then updated the tests. |
| | | |
| | | In our ``TutorialViews`` view class you can see that our two view |
| | | classes are logically grouped together as methods on a common class. |
| | | Since the two views shared the same template, we could move that to a |
| | | ``@view_defaults`` decorator at the class level. |
| | | In our ``TutorialViews`` view class, you can see that our two view classes are |
| | | logically grouped together as methods on a common class. Since the two views |
| | | shared the same template, we could move that to a ``@view_defaults`` decorator |
| | | at the class level. |
| | | |
| | | The tests needed to change. Obviously we needed to import the view |
| | | class. But you can also see the pattern in the tests of instantiating |
| | | the view class with the dummy request first, then calling the view |
| | | method being tested. |
| | | The tests needed to change. Obviously we needed to import the view class. But |
| | | you can also see the pattern in the tests of instantiating the view class with |
| | | the dummy request first, then calling the view method being tested. |
| | | |
| | | .. seealso:: :ref:`class_as_view` |
| | |
| | | |
| | | Organize a views module with decorators and multiple views. |
| | | |
| | | |
| | | Background |
| | | ========== |
| | | |
| | | For the examples so far, the ``hello_world`` function is a "view". In |
| | | Pyramid, views are the primary way to accept web requests and return |
| | | responses. |
| | | For the examples so far, the ``hello_world`` function is a "view". In Pyramid, |
| | | views are the primary way to accept web requests and return responses. |
| | | |
| | | So far our examples place everything in one file: |
| | | |
| | |
| | | |
| | | - The WSGI application launcher |
| | | |
| | | Let's move the views out to their own ``views.py`` module and change |
| | | our startup code to scan that module, looking for decorators that setup |
| | | the views. Let's also add a second view and update our tests. |
| | | Let's move the views out to their own ``views.py`` module and change our |
| | | startup code to scan that module, looking for decorators that set up the views. |
| | | Let's also add a second view and update our tests. |
| | | |
| | | |
| | | Objectives |
| | | ========== |
| | | |
| | | - Views in a module that is scanned by the configurator |
| | | - Move views into a module that is scanned by the configurator. |
| | | |
| | | - Decorators that do declarative configuration |
| | | - Create decorators that do declarative configuration. |
| | | |
| | | |
| | | Steps |
| | | ===== |
| | | |
| | | #. Let's begin by using the previous package as a starting point for a |
| | | new distribution, then making it active: |
| | | #. Let's begin by using the previous package as a starting point for a new |
| | | distribution, then making it active: |
| | | |
| | | .. code-block:: bash |
| | | |
| | |
| | | .. code-block:: bash |
| | | |
| | | |
| | | $ $VENV/bin/nosetests tutorial |
| | | . |
| | | ---------------------------------------------------------------------- |
| | | Ran 4 tests in 0.141s |
| | | |
| | | OK |
| | | $ $VENV/bin/py.test tutorial/tests.py -q |
| | | .... |
| | | 4 passed in 0.28 seconds |
| | | |
| | | #. Run your Pyramid application with: |
| | | |
| | |
| | | #. Open http://localhost:6543/ and http://localhost:6543/howdy |
| | | in your browser. |
| | | |
| | | |
| | | Analysis |
| | | ======== |
| | | |
| | | We added some more URLs, but we also removed the view code from the |
| | | application startup code in ``tutorial/__init__.py``. |
| | | Our views, and their view registrations (via decorators) are now in a |
| | | module ``views.py`` which is scanned via ``config.scan('.views')``. |
| | | We added some more URLs, but we also removed the view code from the application |
| | | startup code in ``tutorial/__init__.py``. Our views, and their view |
| | | registrations (via decorators) are now in a module ``views.py``, which is |
| | | scanned via ``config.scan('.views')``. |
| | | |
| | | We have 2 views, each leading to the other. If you start at |
| | | http://localhost:6543/, you get a response with a link to the next |
| | | view. The ``hello`` view (available at the URL ``/howdy``) has a link |
| | | back to the first view. |
| | | We have two views, each leading to the other. If you start at |
| | | http://localhost:6543/, you get a response with a link to the next view. The |
| | | ``hello`` view (available at the URL ``/howdy``) has a link back to the first |
| | | view. |
| | | |
| | | This step also shows that the name appearing in the URL, |
| | | the name of the "route" that maps a URL to a view, |
| | | and the name of the view, can all be different. More on routes later. |
| | | This step also shows that the name appearing in the URL, the name of the |
| | | "route" that maps a URL to a view, and the name of the view, can all be |
| | | different. More on routes later. |
| | | |
| | | Earlier we saw ``config.add_view`` as one way to configure a view. This |
| | | section introduces ``@view_config``. Pyramid's configuration supports |
| | | :term:`imperative configuration`, such as the |
| | | ``config.add_view`` in the previous example. You can also use |
| | | :term:`declarative configuration`, in which a Python |
| | | :term:`python:decorator` |
| | | is placed on the line above the view. Both approaches result in the |
| | | same final configuration, thus usually, it is simply a matter of taste. |
| | | Earlier we saw ``config.add_view`` as one way to configure a view. This section |
| | | introduces ``@view_config``. Pyramid's configuration supports :term:`imperative |
| | | configuration`, such as the ``config.add_view`` in the previous example. You |
| | | can also use :term:`declarative configuration`, in which a Python |
| | | :term:`python:decorator` is placed on the line above the view. Both approaches |
| | | result in the same final configuration, thus usually, it is simply a matter of |
| | | taste. |
| | | |
| | | Extra Credit |
| | | |
| | | Extra credit |
| | | ============ |
| | | |
| | | #. What does the dot in ``.views`` signify? |
| | | |
| | | #. Why might ``assertIn`` be a better choice in testing the text in |
| | | responses than ``assertEqual``? |
| | | #. Why might ``assertIn`` be a better choice in testing the text in responses |
| | | than ``assertEqual``? |
| | | |
| | | .. seealso:: :ref:`views_chapter`, |
| | | :ref:`view_config_chapter`, and |
| | | :ref:`debugging_view_configuration` |
| | | |