From 674eac946f6dc324f62473a25b4aa77fa5bd3cd8 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 25 Aug 2015 05:04:46 -0700 Subject: [PATCH] Fix #88 --- getting_started/01-helloworld/helloworld.py | 19 -- getting_started/01-helloworld/index.rst | 89 -------- getting_started/02-package/index.rst | 83 ------- getting_started/02-package/setup.py | 13 -- .../02-package/tutorial/__init__.py | 1 - getting_started/02-package/tutorial/views.py | 6 - .../02-package/tutorial/wikiapp.py | 15 -- getting_started/03-config/development.ini | 30 --- getting_started/03-config/index.rst | 103 --------- getting_started/03-config/setup.py | 13 -- .../03-config/tutorial/__init__.py | 8 - getting_started/03-config/tutorial/tests.py | 29 --- getting_started/03-config/tutorial/views.py | 6 - getting_started/04-scaffold/index.rst | 93 -------- getting_started/05-tests/development.ini | 30 --- getting_started/05-tests/index.rst | 94 -------- getting_started/05-tests/setup.py | 13 -- getting_started/05-tests/tutorial/__init__.py | 8 - getting_started/05-tests/tutorial/tests.py | 30 --- getting_started/05-tests/tutorial/views.py | 6 - getting_started/06-template/development.ini | 30 --- getting_started/06-template/index.rst | 113 ---------- getting_started/06-template/setup.py | 14 -- .../06-template/tutorial/__init__.py | 9 - .../tutorial/templates/wiki_view.pt | 10 - getting_started/06-template/tutorial/tests.py | 30 --- getting_started/06-template/tutorial/views.py | 5 - .../07-viewclasses/development.ini | 30 --- getting_started/07-viewclasses/index.rst | 126 ----------- getting_started/07-viewclasses/setup.py | 14 -- .../07-viewclasses/tutorial/__init__.py | 13 -- .../tutorial/templates/layout.pt | 21 -- .../tutorial/templates/wiki_view.pt | 22 -- .../tutorial/templates/wikipage_addedit.pt | 5 - .../tutorial/templates/wikipage_view.pt | 5 - .../07-viewclasses/tutorial/tests.py | 43 ---- .../07-viewclasses/tutorial/views.py | 36 --- getting_started/08-static/development.ini | 30 --- getting_started/08-static/index.rst | 127 ----------- getting_started/08-static/setup.py | 14 -- .../08-static/tutorial/__init__.py | 14 -- .../08-static/tutorial/static/logo.png | Bin 7044 -> 0 bytes .../08-static/tutorial/static/wiki.css | 8 - .../08-static/tutorial/templates/layout.pt | 24 -- .../08-static/tutorial/templates/wiki_view.pt | 13 -- .../tutorial/templates/wikipage_addedit.pt | 5 - .../tutorial/templates/wikipage_view.pt | 12 - getting_started/08-static/tutorial/tests.py | 42 ---- getting_started/08-static/tutorial/views.py | 50 ----- getting_started/09-forms/development.ini | 30 --- getting_started/09-forms/index.rst | 162 -------------- getting_started/09-forms/setup.py | 14 -- getting_started/09-forms/tutorial/__init__.py | 15 -- getting_started/09-forms/tutorial/models.py | 5 - .../09-forms/tutorial/static/logo.png | Bin 7044 -> 0 bytes .../09-forms/tutorial/static/wiki.css | 8 - .../09-forms/tutorial/templates/layout.pt | 25 --- .../09-forms/tutorial/templates/wiki_view.pt | 13 -- .../tutorial/templates/wikipage_addedit.pt | 18 -- .../tutorial/templates/wikipage_view.pt | 12 - getting_started/09-forms/tutorial/tests.py | 42 ---- getting_started/09-forms/tutorial/views.py | 109 --------- getting_started/10-security/development.ini | 30 --- getting_started/10-security/index.rst | 140 ------------ getting_started/10-security/setup.py | 14 -- .../10-security/tutorial/__init__.py | 29 --- .../10-security/tutorial/models.py | 15 -- .../10-security/tutorial/security.py | 8 - .../10-security/tutorial/static/logo.png | Bin 7044 -> 0 bytes .../10-security/tutorial/static/wiki.css | 12 - .../10-security/tutorial/templates/layout.pt | 31 --- .../10-security/tutorial/templates/login.pt | 21 -- .../tutorial/templates/wiki_view.pt | 13 -- .../tutorial/templates/wikipage_addedit.pt | 18 -- .../tutorial/templates/wikipage_view.pt | 12 - getting_started/10-security/tutorial/tests.py | 43 ---- getting_started/10-security/tutorial/views.py | 149 ------------- getting_started/11-sqlalchemy/development.ini | 40 ---- getting_started/11-sqlalchemy/index.rst | 206 ------------------ getting_started/11-sqlalchemy/setup.py | 16 -- .../11-sqlalchemy/sqltutorial.sqlite | Bin 12288 -> 0 bytes .../11-sqlalchemy/tutorial/__init__.py | 36 --- .../11-sqlalchemy/tutorial/models.py | 39 ---- .../tutorial/scripts/__init__.py | 1 - .../tutorial/scripts/initializedb.py | 37 ---- .../11-sqlalchemy/tutorial/security.py | 8 - .../11-sqlalchemy/tutorial/static/logo.png | Bin 7044 -> 0 bytes .../11-sqlalchemy/tutorial/static/wiki.css | 12 - .../tutorial/templates/layout.pt | 30 --- .../11-sqlalchemy/tutorial/templates/login.pt | 21 -- .../tutorial/templates/wiki_view.pt | 13 -- .../tutorial/templates/wikipage_addedit.pt | 18 -- .../tutorial/templates/wikipage_view.pt | 12 - .../11-sqlalchemy/tutorial/tests.py | 59 ----- .../11-sqlalchemy/tutorial/views.py | 152 ------------- getting_started/index.rst | 26 +-- getting_started/pyramid_setup.rst | 27 --- getting_started/python_setup.rst | 78 ------- getting_started/tutorial_approach.rst | 76 ------- 99 files changed, 2 insertions(+), 3447 deletions(-) delete mode 100644 getting_started/01-helloworld/helloworld.py delete mode 100644 getting_started/01-helloworld/index.rst delete mode 100644 getting_started/02-package/index.rst delete mode 100644 getting_started/02-package/setup.py delete mode 100644 getting_started/02-package/tutorial/__init__.py delete mode 100644 getting_started/02-package/tutorial/views.py delete mode 100644 getting_started/02-package/tutorial/wikiapp.py delete mode 100644 getting_started/03-config/development.ini delete mode 100644 getting_started/03-config/index.rst delete mode 100644 getting_started/03-config/setup.py delete mode 100644 getting_started/03-config/tutorial/__init__.py delete mode 100644 getting_started/03-config/tutorial/tests.py delete mode 100644 getting_started/03-config/tutorial/views.py delete mode 100644 getting_started/04-scaffold/index.rst delete mode 100644 getting_started/05-tests/development.ini delete mode 100644 getting_started/05-tests/index.rst delete mode 100644 getting_started/05-tests/setup.py delete mode 100644 getting_started/05-tests/tutorial/__init__.py delete mode 100644 getting_started/05-tests/tutorial/tests.py delete mode 100644 getting_started/05-tests/tutorial/views.py delete mode 100644 getting_started/06-template/development.ini delete mode 100644 getting_started/06-template/index.rst delete mode 100644 getting_started/06-template/setup.py delete mode 100644 getting_started/06-template/tutorial/__init__.py delete mode 100644 getting_started/06-template/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/06-template/tutorial/tests.py delete mode 100644 getting_started/06-template/tutorial/views.py delete mode 100644 getting_started/07-viewclasses/development.ini delete mode 100644 getting_started/07-viewclasses/index.rst delete mode 100644 getting_started/07-viewclasses/setup.py delete mode 100644 getting_started/07-viewclasses/tutorial/__init__.py delete mode 100644 getting_started/07-viewclasses/tutorial/templates/layout.pt delete mode 100644 getting_started/07-viewclasses/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/07-viewclasses/tutorial/templates/wikipage_addedit.pt delete mode 100644 getting_started/07-viewclasses/tutorial/templates/wikipage_view.pt delete mode 100644 getting_started/07-viewclasses/tutorial/tests.py delete mode 100644 getting_started/07-viewclasses/tutorial/views.py delete mode 100644 getting_started/08-static/development.ini delete mode 100644 getting_started/08-static/index.rst delete mode 100644 getting_started/08-static/setup.py delete mode 100644 getting_started/08-static/tutorial/__init__.py delete mode 100644 getting_started/08-static/tutorial/static/logo.png delete mode 100644 getting_started/08-static/tutorial/static/wiki.css delete mode 100644 getting_started/08-static/tutorial/templates/layout.pt delete mode 100644 getting_started/08-static/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/08-static/tutorial/templates/wikipage_addedit.pt delete mode 100644 getting_started/08-static/tutorial/templates/wikipage_view.pt delete mode 100644 getting_started/08-static/tutorial/tests.py delete mode 100644 getting_started/08-static/tutorial/views.py delete mode 100644 getting_started/09-forms/development.ini delete mode 100644 getting_started/09-forms/index.rst delete mode 100644 getting_started/09-forms/setup.py delete mode 100644 getting_started/09-forms/tutorial/__init__.py delete mode 100644 getting_started/09-forms/tutorial/models.py delete mode 100644 getting_started/09-forms/tutorial/static/logo.png delete mode 100644 getting_started/09-forms/tutorial/static/wiki.css delete mode 100644 getting_started/09-forms/tutorial/templates/layout.pt delete mode 100644 getting_started/09-forms/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/09-forms/tutorial/templates/wikipage_addedit.pt delete mode 100644 getting_started/09-forms/tutorial/templates/wikipage_view.pt delete mode 100644 getting_started/09-forms/tutorial/tests.py delete mode 100644 getting_started/09-forms/tutorial/views.py delete mode 100644 getting_started/10-security/development.ini delete mode 100644 getting_started/10-security/index.rst delete mode 100644 getting_started/10-security/setup.py delete mode 100644 getting_started/10-security/tutorial/__init__.py delete mode 100644 getting_started/10-security/tutorial/models.py delete mode 100644 getting_started/10-security/tutorial/security.py delete mode 100644 getting_started/10-security/tutorial/static/logo.png delete mode 100644 getting_started/10-security/tutorial/static/wiki.css delete mode 100644 getting_started/10-security/tutorial/templates/layout.pt delete mode 100644 getting_started/10-security/tutorial/templates/login.pt delete mode 100644 getting_started/10-security/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/10-security/tutorial/templates/wikipage_addedit.pt delete mode 100644 getting_started/10-security/tutorial/templates/wikipage_view.pt delete mode 100644 getting_started/10-security/tutorial/tests.py delete mode 100644 getting_started/10-security/tutorial/views.py delete mode 100644 getting_started/11-sqlalchemy/development.ini delete mode 100644 getting_started/11-sqlalchemy/index.rst delete mode 100644 getting_started/11-sqlalchemy/setup.py delete mode 100644 getting_started/11-sqlalchemy/sqltutorial.sqlite delete mode 100644 getting_started/11-sqlalchemy/tutorial/__init__.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/models.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/scripts/__init__.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/scripts/initializedb.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/security.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/static/logo.png delete mode 100644 getting_started/11-sqlalchemy/tutorial/static/wiki.css delete mode 100644 getting_started/11-sqlalchemy/tutorial/templates/layout.pt delete mode 100644 getting_started/11-sqlalchemy/tutorial/templates/login.pt delete mode 100644 getting_started/11-sqlalchemy/tutorial/templates/wiki_view.pt delete mode 100644 getting_started/11-sqlalchemy/tutorial/templates/wikipage_addedit.pt delete mode 100644 getting_started/11-sqlalchemy/tutorial/templates/wikipage_view.pt delete mode 100644 getting_started/11-sqlalchemy/tutorial/tests.py delete mode 100644 getting_started/11-sqlalchemy/tutorial/views.py delete mode 100644 getting_started/pyramid_setup.rst delete mode 100644 getting_started/python_setup.rst delete mode 100644 getting_started/tutorial_approach.rst diff --git a/getting_started/01-helloworld/helloworld.py b/getting_started/01-helloworld/helloworld.py deleted file mode 100644 index 9ebb604..0000000 --- a/getting_started/01-helloworld/helloworld.py +++ /dev/null @@ -1,19 +0,0 @@ -from wsgiref.simple_server import make_server -from pyramid.config import Configurator -from pyramid.response import Response - -def hello_world(request): - return Response('Hello') - -def main(): - config = Configurator() - config.add_route('hello', '/') - config.add_view(hello_world, route_name='hello') - app = config.make_wsgi_app() - return app - -if __name__ == '__main__': - app = main() - server = make_server('0.0.0.0', 6547, app) - print ('Starting up server on http://localhost:6547') - server.serve_forever() \ No newline at end of file diff --git a/getting_started/01-helloworld/index.rst b/getting_started/01-helloworld/index.rst deleted file mode 100644 index 10214bc..0000000 --- a/getting_started/01-helloworld/index.rst +++ /dev/null @@ -1,89 +0,0 @@ -================================ -1: Single-File WSGI Applications -================================ - -What's the simplest way to get started in Pyramid? A single-file module. -No packages, ``setup.py``, or other machinery. - -Objectives -========== - -- Get Pyramid pixels on the screen as easily as possible - -- Use that as a well-understood base for adding each unit of complexity - -- Create a module with a view that acts as an HTTP server - -- Visit the URL in your browser - -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*. - -Pyramid is special because it can act as a single-file module -microframework. You have a single Python file that can be executed -directly by Python. But Pyramid also scales to the largest of -applications. - -Steps -===== - -#. Make sure you have followed the steps in :doc:`../python_setup`. - -#. Create a directory for this step: - - .. code-block:: bash - - (env33)$ mkdir step01; cd step01 - -#. Copy the following into ``helloworld.py``: - - .. literalinclude:: helloworld.py - :linenos: - -#. Run the application: - - .. code-block:: bash - - (env33)$ python3.3 helloworld.py - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -Analysis -======== - -The ``main()`` function is run from the ``if`` at the bottom, -which makes a WSGI application, hands it to an HTTP server (the -pure-Python server in the Python standard library), and starts listening. - -This single-file module does quite a bit for so few lines, -thus making it spiritually similar to microframeworks. A view function -is added to the configuration. When called, the view returns a response. - -The ``hello_world`` view is mapped to a route which is matched to the root -URL of the application. - -Extra Credit -============ - -#. Why do we do this: - - .. code-block:: python - - print ('Starting up server on http://localhost:6547') - - ...instead of: - - .. code-block:: python - - print 'Starting up server on http://localhost:6547' - -#. 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 helloworld.py`` and restart, - then reload your browser. See the exception in the console? diff --git a/getting_started/02-package/index.rst b/getting_started/02-package/index.rst deleted file mode 100644 index 8678017..0000000 --- a/getting_started/02-package/index.rst +++ /dev/null @@ -1,83 +0,0 @@ -============================================== -2: Packaging for the Wiki Tutorial Application -============================================== - -Most modern Python development is done using Python packages, -an approach Pyramid puts to good use. In this step, we start writing our -Wiki application as a standard Python package. - -Objectives -========== - -- Get a minimum Python package in place by making a ``setup.py`` - -- Get our basic directory structure in place - -- Install our ``tutorial`` package - -Steps -===== - -#. Use the previous step as the basis for this step: - - .. code-block:: bash - - (env33)$ cd ..; mkdir step02; cd step02 - -#. In ``setup.py``, enter the following: - - .. literalinclude:: setup.py - :linenos: - -#. Make the new package installed for development then make a directory - for the actual code: - - .. code-block:: bash - - (env33)$ python3.3 setup.py develop - (env33)$ mkdir tutorial - -#. Enter the following into ``tutorial/__init__.py``: - - .. literalinclude:: tutorial/__init__.py - -#. Enter the following into ``tutorial/wikiapp.py``: - - .. literalinclude:: tutorial/wikiapp.py - -#. Enter the following into ``tutorial/views.py``: - - .. literalinclude:: tutorial/views.py - -#. Run the WSGI application with: - - .. code-block:: bash - - (env33)$ python3.3 tutorial/wikiapp.py - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -Analysis -======== - -In this case we have a Python package called ``tutorial``. We use the -same name in each step of the Wiki tutorial, to avoid unnecessary -re-typing. - -We moved our views out to a second file ``views.py``. We then told the -``Configurator`` to go scan for anything that looks like a -configuration instruction, such as the ``@view_config`` decorator. - -Extra Credit -============ - -#. Why do we make an extra hop in the directory with ``tutorial``? - -#. Could we have eliminated ``wikiapp.py`` and put the WSGI - application startup in ``__init__.py``? How would that have affected - the command used to start the application? - -#. The previous example used ``config.add_view``. This example uses a - ``@view_config`` decorator. Does Pyramid treat the imperative - (``add_view``) configuration differently than the declarative - (``@view_config``) approach? diff --git a/getting_started/02-package/setup.py b/getting_started/02-package/setup.py deleted file mode 100644 index c635842..0000000 --- a/getting_started/02-package/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', -] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, -) diff --git a/getting_started/02-package/tutorial/__init__.py b/getting_started/02-package/tutorial/__init__.py deleted file mode 100644 index d310fdd..0000000 --- a/getting_started/02-package/tutorial/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package \ No newline at end of file diff --git a/getting_started/02-package/tutorial/views.py b/getting_started/02-package/tutorial/views.py deleted file mode 100644 index b3300c8..0000000 --- a/getting_started/02-package/tutorial/views.py +++ /dev/null @@ -1,6 +0,0 @@ -from pyramid.response import Response -from pyramid.view import view_config - -@view_config(route_name='hello') -def hello_world(request): - return Response('Hello') diff --git a/getting_started/02-package/tutorial/wikiapp.py b/getting_started/02-package/tutorial/wikiapp.py deleted file mode 100644 index 51c7376..0000000 --- a/getting_started/02-package/tutorial/wikiapp.py +++ /dev/null @@ -1,15 +0,0 @@ -from wsgiref.simple_server import make_server -from pyramid.config import Configurator - -def main(): - config = Configurator() - config.add_route('hello', '/') - config.scan('views') - app = config.make_wsgi_app() - return app - -if __name__ == '__main__': - app = main() - server = make_server('0.0.0.0', 6547, app) - print ('Starting up server on http://localhost:6547') - server.serve_forever() \ No newline at end of file diff --git a/getting_started/03-config/development.ini b/getting_started/03-config/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/03-config/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/03-config/index.rst b/getting_started/03-config/index.rst deleted file mode 100644 index 5c67297..0000000 --- a/getting_started/03-config/index.rst +++ /dev/null @@ -1,103 +0,0 @@ -================================= -3: Configuration Files and pserve -================================= - -Pyramid has a first-class concept of -:ref:`configuration ` distinct from code. -This approach is optional, but its presence makes it distinct from -other Python web frameworks. - -Objectives -========== - -- Create an application driven by a ``.ini`` file - -- Startup the application with Pyramid's ``pserve`` command - -- Move code into the package's ``__init__.py`` - -Steps -===== - -#. Let's begin using the previous package as a starting point for a new - distribution, then making it active: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step02 step03; cd step03 - (env33)$ python3.3 setup.py develop - -#. Pyramid can use a .ini-formatted configuration file as its launching - point. Type the following into ``development.ini`` inside - ``step03``: - - .. literalinclude:: development.ini - :linenos: - -#. Many Pyramid apps put the logic for setting up the WSGI app into the - distribution's ``__init__.py``, so enter the following into - ``tutorial/__init__.py``: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. Let's eliminate our ``wikiapp.py``, as its functionality is now - merged into ``__init__.py``: - - .. code-block:: bash - - (env33)$ rm tutorial/wikiapp.py* - - We do ``.py*`` to ensure we get the byte-compiled ``.pyc`` file. - -#. Run the WSGI application with:: - - (env33)$ pserve development.ini - -#. Open http://127.0.0.1:6547/ in your browser. - -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 -:ref:`application startup `: - -- ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial`` - -- The package's ``setup.py`` has defined this (lines 9-10) for the - distribution (egg) as ``tutorial:main`` - -- The ``tutorial`` distribution's ``__init__`` has a ``main`` function - -- This function is invoked, with the values from certain ``.ini`` - sections passed in - -The ``.ini`` file is also used for two other functions: - -- ``[server:main]`` wires up the choice of WSGI *server* for your WSGI - *application* - -- Pyramid uses Python standard logging, which needs a number of - configuration values. The ``.ini`` serves this function. - -In this case, ``pserve`` finds the server -defined by ``[server:main]`` and the application defined by -``[app:main]``. The latter's ``use = egg:tutorial`` governs - -The code from ``wikiapp.py`` that launched our application not only -was moved to ``__init__.py``...it also got shorter, -as it didn't need to wire up a WSGI server. - -Extra Credit -============ - -#. Is it possible to develop your application separately from the - configuration? Is that a good idea? - -#. Can you have more than one ``.ini`` file for an application? - -#. If you don't like configuration and/or ``.ini`` files, - could you do this yourself in Python code? - diff --git a/getting_started/03-config/setup.py b/getting_started/03-config/setup.py deleted file mode 100644 index c986ce5..0000000 --- a/getting_started/03-config/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - ] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, - ) diff --git a/getting_started/03-config/tutorial/__init__.py b/getting_started/03-config/tutorial/__init__.py deleted file mode 100644 index 0476466..0000000 --- a/getting_started/03-config/tutorial/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.add_route('hello', '/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/03-config/tutorial/tests.py b/getting_started/03-config/tutorial/tests.py deleted file mode 100644 index 8817d9f..0000000 --- a/getting_started/03-config/tutorial/tests.py +++ /dev/null @@ -1,29 +0,0 @@ -import unittest - -from pyramid import testing - - -class ViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_my_view(self): - from tutorial.views import hello_world - request = testing.DummyRequest() - response = hello_world(request) - self.assertEqual(response.status, '200 OK') - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - settings = {} - app = main(settings) - from webtest import TestApp - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Hello', res.body) diff --git a/getting_started/03-config/tutorial/views.py b/getting_started/03-config/tutorial/views.py deleted file mode 100644 index b3300c8..0000000 --- a/getting_started/03-config/tutorial/views.py +++ /dev/null @@ -1,6 +0,0 @@ -from pyramid.response import Response -from pyramid.view import view_config - -@view_config(route_name='hello') -def hello_world(request): - return Response('Hello') diff --git a/getting_started/04-scaffold/index.rst b/getting_started/04-scaffold/index.rst deleted file mode 100644 index 84d1e0d..0000000 --- a/getting_started/04-scaffold/index.rst +++ /dev/null @@ -1,93 +0,0 @@ -===================================== -4: Quick Project Start With Scaffolds -===================================== - -Getting started quickly on a Python application requires, -as we just saw, several steps. Like other popular web frameworks, -Pyramid provides a facility called "scaffolds" that let you quickly -generate the basics of a Pyramid application. - -Objectives -========== - -- Use ``pcreate`` to see what scaffolds are available - -- Create and install a sample project using one of the available - scaffolds - -Steps -===== - -#. We don't need to copy the previous step. Instead, - just make an empty new directory: - - .. code-block:: bash - - (env33)$ cd ..; mkdir step04; cd step04 - -#. See the usage for ``pcreate``: - - .. code-block:: bash - - (env33)$ pcreate --help - -#. List the available scaffolds: - - .. code-block:: bash - - (env33)$ pcreate --list - -#. Make a new Python project called ``tutorial`` using ``starter`` - as a starting point: - - .. code-block:: bash - - (env33)$ pcreate -s starter tutorial - - -#. Visit that project and see the project contents: - - .. code-block:: bash - - (env33)$ cd tutorial; ls - -#. Install your new project: - - .. code-block:: bash - - (env33)$ python3.3 setup.py develop - -#. Run the WSGI application with: - - .. code-block:: bash - - (env33)$ pserve development.ini - -#. Open ``http://127.0.0.1:6543/`` in your browser. *Different port - number!* - -Analysis -======== - -Pyramid has a number of scaffold *templates* that provide different -starting points. ``starter`` is the most basic. ``alchemy`` is useful -for developers wanting SQLAlchemy as a starting point. These scaffolds -provide a way for Pyramid to remain unopinionated, -but still provide starting points with a set of opinions. - -When you visited this project in your browser, you might have seen the -``pyramid_debugtoolbar`` in action. This is a handy helper during -development. - -.. note:: - - The remaining steps in this tutorial do not use scaffolds, - as we want to teach the various decisions being made. - -Extra Credit -============ - -#. If you make a project with one of the scaffolds, can you still - share your project with others? - -#. Can you make scaffolds for your own Pyramid projects? diff --git a/getting_started/05-tests/development.ini b/getting_started/05-tests/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/05-tests/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/05-tests/index.rst b/getting_started/05-tests/index.rst deleted file mode 100644 index 5f47ed3..0000000 --- a/getting_started/05-tests/index.rst +++ /dev/null @@ -1,94 +0,0 @@ -============================ -5: Unit and Functional Tests -============================ - -Test coverage is a badge of honor for Pyramid, which has 100% test (and -documentation) coverage from its inception. Equally, -Pyramid wants to help Pyramid developers be productive in writing tests -for their code, providing facilities to promote this. - -Pyramid has a first-class concept of -:ref:`configuration ` distinct from code. -This approach is optional, but its presence makes it distinct from -other Python web frameworks. - -Sure, testing helps ensure future quality and facilitate refactoring. -But it also makes up-front development faster, particularly in smart -editors and IDEs. Restarting your app and clicky-clicking in your -browser is a drag. - -Objectives -========== - -- Provide both :ref:`unit ` and - :ref:`functional ` tests for our - Wiki application - -- Create a new module ``tutorial/tests.py`` - -- Write unit tests and write functional tests using - `WebTest `_ - -- Run both under the - `nose `_ test runner - -Steps -===== - -#. Again, let's use the previous package as a starting point for a new - distribution, then making it active. We will also install ``nose`` - and ``WebTest``: - - .. code-block:: bash - - (env33)$ cd ../..; cp -r step03 step05; cd step05 - (env33)$ python3.3 setup.py develop - (env33)$ easy_install-3.3 nose webtest - -#. Create a module for our unit and functional tests in - ``tutorial/tests.py``: - - .. literalinclude:: tutorial/tests.py - :linenos: - -#. Now run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -Analysis -======== - -Our unit tests are first. We have one unit test, which simply executes -our view and ensures it returns the proper status code. - -Our functional test is second. To run, WebTest needs to be pointed at -the callable that returns our WSGI application. We then execute a web -request at the ``/`` URL and ensure that it returns a body with a -string we are expecting. - -Extra Credit -============ - -#. How does ``nose`` know the tests are in ``tests.py``? - -#. Did WebTest really launch an HTTP server and issue an HTTP request? - -#. If your code generates an error, will Pyramid handle it gracefully? - -Discussion -========== - -- Pyramid and the commitment to test coverage - -- Philosophies on unit tests vs. integration tests vs. - functional tests vs. doctests - -- The challenge in setup/teardown regarding configuration, registries, - and machinery under the surface (both the frameworks *and* yours!) diff --git a/getting_started/05-tests/setup.py b/getting_started/05-tests/setup.py deleted file mode 100644 index c635842..0000000 --- a/getting_started/05-tests/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', -] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, -) diff --git a/getting_started/05-tests/tutorial/__init__.py b/getting_started/05-tests/tutorial/__init__.py deleted file mode 100644 index 0476466..0000000 --- a/getting_started/05-tests/tutorial/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.add_route('hello', '/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/05-tests/tutorial/tests.py b/getting_started/05-tests/tutorial/tests.py deleted file mode 100644 index 6bf5acc..0000000 --- a/getting_started/05-tests/tutorial/tests.py +++ /dev/null @@ -1,30 +0,0 @@ -import unittest - -from pyramid import testing - - -class ViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_my_view(self): - from tutorial.views import hello_world - request = testing.DummyRequest() - response = hello_world(request) - self.assertEqual(response.status, '200 OK') - - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - settings = {} - app = main(settings) - from webtest import TestApp - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Hello', res.body) diff --git a/getting_started/05-tests/tutorial/views.py b/getting_started/05-tests/tutorial/views.py deleted file mode 100644 index b3300c8..0000000 --- a/getting_started/05-tests/tutorial/views.py +++ /dev/null @@ -1,6 +0,0 @@ -from pyramid.response import Response -from pyramid.view import view_config - -@view_config(route_name='hello') -def hello_world(request): - return Response('Hello') diff --git a/getting_started/06-template/development.ini b/getting_started/06-template/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/06-template/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/06-template/index.rst b/getting_started/06-template/index.rst deleted file mode 100644 index 6e7d876..0000000 --- a/getting_started/06-template/index.rst +++ /dev/null @@ -1,113 +0,0 @@ -============= -6: Templating -============= - -Web development usually involves HTML, and web frameworks generally -provide support for HTML generation using templating languages. - -Pyramid provides bundled support for the Chameleon and Mako templating -engines, with active community support for bindings such as Jinja2. - -Objectives -========== - -- Create a template using Chameleon and associate it with a view - -- Change our tests to reflect how we test when views don't render HTML - directly - -Steps -===== - -#. Again, let's use the previous package as a starting point for a new - distribution. Also, make a directory for the templates: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step05 step06; cd step06 - (env33)$ python3.3 setup.py develop - (env33)$ mkdir tutorial/templates - -#. Since we are using Chameleon, ``tutorial/__init__.py`` needs an additional - configuration: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. Our ``tutorial/views.py`` is now data-centric: - - .. literalinclude:: tutorial/views.py - :linenos: - -#. Create a Chameleon template in ``tutorial/templates/wiki_view.pt``: - - .. literalinclude:: tutorial/templates/wiki_view.pt - :language: html - :linenos: - -#. Our ``tutorial/tests.py`` has a unit test which is also now - data-centric: - - .. literalinclude:: tutorial/tests.py - :linenos: - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application. Note the ``--reload``: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -#. Edit the template and reload your browser to see the changes. Do - the same on the ``title`` returned in ``views.py``. - -Analysis -======== - -Our view function changed significantly. It is now a callable that -returns data, which makes test-writing much more meaningful. Our -``@view_config`` wraps the view with a ``renderer`` that is pointed at -a Chameleon template. Pyramid knows from the file suffix ``.pt`` that -this should use the Chameleon engine for rendering the response. - -The use of a ``templates`` directory is purely a matter of taste. You -don't have to have a magically-named directory, or any subdirectory at -all. - -Each kind of renderer (Chameleon, Mako, JSON, add-on renderers such as -Jinja) manage what goes into the namespace of the template. Chameleon -provides ``request`` automatically, for example, as well as the data -returned from the view. - -The ``--reload`` argument to ``pserve`` makes it watch for changes to -certain kinds of files. For example, if you change a ``.py`` file, -the application will restart automatically. - -Extra Credit -============ - -#. Will the application restart if I change my ``development.ini`` - configuration file? Give it a try. - -#. What if I wanted to use Mako? Jinja2? Some brand new templating - language? - -#. There was a little bit of lag as I visited some views for the first - time. What do you think was happening? - -#. Can I write and register my own renderers? Should I? Can I share the - renderers with other people? - - diff --git a/getting_started/06-template/setup.py b/getting_started/06-template/setup.py deleted file mode 100644 index ff2d36b..0000000 --- a/getting_started/06-template/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon', - ] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, - ) diff --git a/getting_started/06-template/tutorial/__init__.py b/getting_started/06-template/tutorial/__init__.py deleted file mode 100644 index 05219a7..0000000 --- a/getting_started/06-template/tutorial/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.include('pyramid_chameleon') - config.add_route('hello', '/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/06-template/tutorial/templates/wiki_view.pt b/getting_started/06-template/tutorial/templates/wiki_view.pt deleted file mode 100644 index 9f97058..0000000 --- a/getting_started/06-template/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,10 +0,0 @@ - - - ${title} - - -
-

${title}

-
- - diff --git a/getting_started/06-template/tutorial/tests.py b/getting_started/06-template/tutorial/tests.py deleted file mode 100644 index 8b17729..0000000 --- a/getting_started/06-template/tutorial/tests.py +++ /dev/null @@ -1,30 +0,0 @@ -import unittest - -from pyramid import testing - - -class ViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_my_view(self): - from tutorial.views import hello_world - request = testing.DummyRequest() - response = hello_world(request) - self.assertEqual(response['title'], 'Hello World') - - -class FunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - settings = {} - app = main(settings) - from webtest import TestApp - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Hello', res.body) diff --git a/getting_started/06-template/tutorial/views.py b/getting_started/06-template/tutorial/views.py deleted file mode 100644 index 47d75df..0000000 --- a/getting_started/06-template/tutorial/views.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyramid.view import view_config - -@view_config(route_name='hello', renderer='templates/wiki_view.pt') -def hello_world(request): - return dict(title='Hello World') diff --git a/getting_started/07-viewclasses/development.ini b/getting_started/07-viewclasses/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/07-viewclasses/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/07-viewclasses/index.rst b/getting_started/07-viewclasses/index.rst deleted file mode 100644 index 602af9c..0000000 --- a/getting_started/07-viewclasses/index.rst +++ /dev/null @@ -1,126 +0,0 @@ -=============== -7: View Classes -=============== - -Free-standing functions are the simple way to do views. Many times, -though, you have several views that are closely related. For example, -a wiki might have many different ways to look at it (home page, add, -view, edit, delete, table of contents, etc.) - -For some people, grouping these together makes logical sense. A view -class lets you group these views, sharing some state assignments and -helper functions as class methods. - -Objectives -========== - -- Introduce a view class for our wiki-related views - -- Start making this more like our wiki application by adding more - views and templates - -Steps -===== - -#. Let's again use the previous package as a starting point for a new - distribution: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step06 step07; cd step07 - (env33)$ python3.3 setup.py develop - -#. Edit ``tutorial/views.py`` to include a view class with several more - views, along with a "layout" template: - - .. literalinclude:: tutorial/views.py - :linenos: - -#. Since we have new views, our ``__init__.py`` needs new routes: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. Now we make some new templates. First, - ``tutorial/templates/layout.pt``: - - .. literalinclude:: tutorial/templates/layout.pt - :linenos: - :language: html - -#. Next, ``tutorial/templates/wiki_view.pt``: - - .. literalinclude:: tutorial/templates/wiki_view.pt - :linenos: - :language: html - -#. Next, ``tutorial/templates/wikipage_addedit.pt``: - - .. literalinclude:: tutorial/templates/wikipage_addedit.pt - :linenos: - :language: html - -#. Finally, ``tutorial/templates/wikipage_view.pt``: - - .. literalinclude:: tutorial/templates/wikipage_view.pt - :linenos: - :language: html - -#. Since we have more views, we need more tests in - ``tutorial/tests.py``: - - .. literalinclude:: tutorial/tests.py - :linenos: - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -Analysis -======== - -Our ``__init__`` shows us a new feature we are using with our -``add_route`` configurations: *named parameters*. These variables, -indicated with curly braces, later become available on -``request.matchdict``. - -We are adding multiple views to our site now. Rather than repeat the -same stuff in ```` and the other "chrome" common to all -templates, we make a master template available in the view class as -``layout``. Attributes and methods of the view class instance are -available inside the template from the ``view`` variable, -as we saw with ``view.layout``. - -The master template defines slots that can be filled by the view -templates. Our ``layout.pt`` used ``metal:define-slot="content"`` to -make one such slot. Each view template filled this with -``metal:fill-slot="content"``. - -Our ``wikipage_delete`` returns an ``HTTPFound``. This is Pyramid's way -of issuing a redirect. - -In our unit tests we have to add an extra step to create an instance of -the view class, and then call the view being tested. - -Extra Credit -============ - -#. If we wanted to make a set of statements to several views in a view - class at once, how would we do that? - -#. Can I put multiple named parameters in my routes? diff --git a/getting_started/07-viewclasses/setup.py b/getting_started/07-viewclasses/setup.py deleted file mode 100644 index ff2d36b..0000000 --- a/getting_started/07-viewclasses/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon', - ] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, - ) diff --git a/getting_started/07-viewclasses/tutorial/__init__.py b/getting_started/07-viewclasses/tutorial/__init__.py deleted file mode 100644 index 1f746bb..0000000 --- a/getting_started/07-viewclasses/tutorial/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.include('pyramid_chameleon') - config.add_route('wiki_view', '/') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_route('wikipage_delete', '/{uid}/delete') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/07-viewclasses/tutorial/templates/layout.pt b/getting_started/07-viewclasses/tutorial/templates/layout.pt deleted file mode 100644 index 7eb9faa..0000000 --- a/getting_started/07-viewclasses/tutorial/templates/layout.pt +++ /dev/null @@ -1,21 +0,0 @@ - - - - Wiki - ${title} - - -
-
- Wiki Home -
-

${title}

- -
-
-
- - \ No newline at end of file diff --git a/getting_started/07-viewclasses/tutorial/templates/wiki_view.pt b/getting_started/07-viewclasses/tutorial/templates/wiki_view.pt deleted file mode 100644 index 7e1e202..0000000 --- a/getting_started/07-viewclasses/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,22 +0,0 @@ -
-
-

Some sample links:

- -
-
\ No newline at end of file diff --git a/getting_started/07-viewclasses/tutorial/templates/wikipage_addedit.pt b/getting_started/07-viewclasses/tutorial/templates/wikipage_addedit.pt deleted file mode 100644 index d890e4f..0000000 --- a/getting_started/07-viewclasses/tutorial/templates/wikipage_addedit.pt +++ /dev/null @@ -1,5 +0,0 @@ -
-
-

Content here.

-
-
\ No newline at end of file diff --git a/getting_started/07-viewclasses/tutorial/templates/wikipage_view.pt b/getting_started/07-viewclasses/tutorial/templates/wikipage_view.pt deleted file mode 100644 index 6c43aef..0000000 --- a/getting_started/07-viewclasses/tutorial/templates/wikipage_view.pt +++ /dev/null @@ -1,5 +0,0 @@ -
-
-

Content here for uid ${uid}.

-
-
\ No newline at end of file diff --git a/getting_started/07-viewclasses/tutorial/tests.py b/getting_started/07-viewclasses/tutorial/tests.py deleted file mode 100644 index daea64e..0000000 --- a/getting_started/07-viewclasses/tutorial/tests.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest - -from pyramid import testing - - -class WikiViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - self.config.include('pyramid_chameleon') - - def tearDown(self): - testing.tearDown() - - def test_wiki_view(self): - from tutorial.views import WikiViews - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(response['title'], 'Welcome to the Wiki') - - -class WikiFunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - - settings = {} - app = main(settings) - from webtest import TestApp - - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Welcome', res.body) - res = self.testapp.get('/add', status=200) - self.assertIn(b'Add Wiki Page', res.body) - res = self.testapp.get('/100', status=200) - self.assertIn(b'100', res.body) - res = self.testapp.get('/100/edit', status=200) - self.assertIn(b'Edit', res.body) - res = self.testapp.get('/100/delete', status=302) - self.assertIn(b'Found', res.body) diff --git a/getting_started/07-viewclasses/tutorial/views.py b/getting_started/07-viewclasses/tutorial/views.py deleted file mode 100644 index 375e070..0000000 --- a/getting_started/07-viewclasses/tutorial/views.py +++ /dev/null @@ -1,36 +0,0 @@ -from pyramid.httpexceptions import HTTPFound -from pyramid.renderers import get_renderer -from pyramid.view import view_config - - -class WikiViews(object): - def __init__(self, request): - self.request = request - renderer = get_renderer("templates/layout.pt") - self.layout = renderer.implementation().macros['layout'] - - @view_config(route_name='wiki_view', - renderer='templates/wiki_view.pt') - def wiki_view(self): - return dict(title='Welcome to the Wiki') - - @view_config(route_name='wikipage_add', - renderer='templates/wikipage_addedit.pt') - def wikipage_add(self): - return dict(title='Add Wiki Page') - - @view_config(route_name='wikipage_view', - renderer='templates/wikipage_view.pt') - def wikipage_view(self): - uid = self.request.matchdict['uid'] - return dict(title='View Wiki Page', uid=uid) - - @view_config(route_name='wikipage_edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_edit(self): - return dict(title='Edit Wiki Page') - - @view_config(route_name='wikipage_delete') - def wikipage_delete(self): - return HTTPFound('/') - diff --git a/getting_started/08-static/development.ini b/getting_started/08-static/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/08-static/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/08-static/index.rst b/getting_started/08-static/index.rst deleted file mode 100644 index bb40f17..0000000 --- a/getting_started/08-static/index.rst +++ /dev/null @@ -1,127 +0,0 @@ -================ -8: Static Assets -================ - -Web applications include many static assets in the UX: CSS and JS files, -images, etc. Web frameworks need to support productive development by -the UX team, but also the richness and complexity required by the core -developers and the deployment team. - -It’s a surprisingly hard problem, supporting all these needs while -keeping the simple case easy. - -Pyramid accomplishes this using the view machinery and static assets. - -Objectives -========== - -- Use ``add_static_view`` serve a project directory at a URL - -- Introduce some dummy data to provide dynamicism - -Steps -===== - -#. Again, let's use the previous package as a starting point for a new - distribution, plus make a new directory for the static assets: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step07 step08; cd step08 - (env33)$ mkdir tutorial/static - (env33)$ python3.3 setup.py develop - -#. The ``Configurator`` needs to be told in ``tutorial/__init__.py`` - about the static files by - calling its ``add_static_view``: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. Our ``tutorial/views.py`` has some extras with dummy data driving - the listing and viewing: - - .. literalinclude:: tutorial/views.py - :linenos: - -#. Make a ``tutorial/static/wiki.css`` for the styling: - - .. literalinclude:: tutorial/static/wiki.css - :language: css - -#. We also have a :download:`logo PNG file ` - that we need saved at ``tutorial/static/logo.png``. Click this link - to download and save the image. - -#. The ``tutorial/templates/layout.pt`` includes the CSS on all pages, - plus adds a logo that goes back to the wiki home: - - .. literalinclude:: tutorial/templates/layout.pt - :linenos: - :language: html - -#. ``tutorial/templates/wiki_view.pt`` switches to a more-general - syntax for computing URLs, plus iterates over the actual (dummy) data - for listing each wiki page: - - .. literalinclude:: tutorial/templates/wiki_view.pt - :linenos: - :language: html - -#. We also change ``tutorial/templates/wikipage_view.pt`` to use the - ``route_url`` approach to URLs: - - .. literalinclude:: tutorial/templates/wikipage_view.pt - :linenos: - :language: html - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -Analysis -======== - -We made files and directories in ``tutorial/static`` available at the -URL ``static``. However, we used ``tutorial:static`` as the argument in -``add_static_view``. Pyramid uses a robust scheme called *asset -specifications* to work with static assets. - -In our templates, we resolved the full path to a static asset in a -package by using ``request.static_url`` and passing in an asset -specification. ``route_url``, ``static_url``, and friends let you -refactor your URL structure, or even publish to a different root URL, -without breaking the links in your templates. - -Finally, we're cheating by having mutable dummy data at module scope. -We will replace this shortly with database-driven data. - -Extra Credit -============ - -#. Can you use ``add_static_view`` to serve up a directory listing with - links to the contents in a directory? - -#. Does Pyramid have support for setting cache parameters on static - assets? - -#. Can you also use asset specifications when naming the template for a - view? - -#. Can I provide a one-liner for including static assets in my Pyramid - libraries? \ No newline at end of file diff --git a/getting_started/08-static/setup.py b/getting_started/08-static/setup.py deleted file mode 100644 index 10ca94a..0000000 --- a/getting_started/08-static/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon' - ] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, - ) diff --git a/getting_started/08-static/tutorial/__init__.py b/getting_started/08-static/tutorial/__init__.py deleted file mode 100644 index 75d25f1..0000000 --- a/getting_started/08-static/tutorial/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.include('pyramid_chameleon') - config.add_route('wiki_view', '/') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_route('wikipage_delete', '/{uid}/delete') - config.add_static_view(name='static', path='tutorial:static') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/08-static/tutorial/static/logo.png b/getting_started/08-static/tutorial/static/logo.png deleted file mode 100644 index a5bc0ade71d3da5eab67391e840ff20448c42cd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7044 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 i2&XM_{d<%B#s3F+!sT<+TNZKv0000 - - - Wiki - ${title} - - - -
-

- - Logo - ${title}

- -
-
-
- - \ No newline at end of file diff --git a/getting_started/08-static/tutorial/templates/wiki_view.pt b/getting_started/08-static/tutorial/templates/wiki_view.pt deleted file mode 100644 index 149d469..0000000 --- a/getting_started/08-static/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,13 +0,0 @@ - \ No newline at end of file diff --git a/getting_started/08-static/tutorial/templates/wikipage_addedit.pt b/getting_started/08-static/tutorial/templates/wikipage_addedit.pt deleted file mode 100644 index d890e4f..0000000 --- a/getting_started/08-static/tutorial/templates/wikipage_addedit.pt +++ /dev/null @@ -1,5 +0,0 @@ -
-
-

Content here.

-
-
\ No newline at end of file diff --git a/getting_started/08-static/tutorial/templates/wikipage_view.pt b/getting_started/08-static/tutorial/templates/wikipage_view.pt deleted file mode 100644 index 4cbd946..0000000 --- a/getting_started/08-static/tutorial/templates/wikipage_view.pt +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - Edit - | - - Delete - - -

${structure: page.body}

-
-
\ No newline at end of file diff --git a/getting_started/08-static/tutorial/tests.py b/getting_started/08-static/tutorial/tests.py deleted file mode 100644 index 07e9861..0000000 --- a/getting_started/08-static/tutorial/tests.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest - -from pyramid import testing - - -class WikiViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_wiki_view(self): - from tutorial.views import WikiViews - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(response['title'], 'Welcome to the Wiki') - - -class WikiFunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - - settings = {} - app = main(settings) - from webtest import TestApp - - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Welcome', res.body) - res = self.testapp.get('/add', status=200) - self.assertIn(b'Add Wiki Page', res.body) - res = self.testapp.get('/100', status=200) - self.assertIn(b'100', res.body) - res = self.testapp.get('/100/edit', status=200) - self.assertIn(b'Edit', res.body) - res = self.testapp.get('/100/delete', status=302) - self.assertIn(b'Found', res.body) diff --git a/getting_started/08-static/tutorial/views.py b/getting_started/08-static/tutorial/views.py deleted file mode 100644 index fecf45c..0000000 --- a/getting_started/08-static/tutorial/views.py +++ /dev/null @@ -1,50 +0,0 @@ -from pyramid.httpexceptions import HTTPFound -from pyramid.renderers import get_renderer -from pyramid.view import view_config - -pages = [ - dict(uid='100', title='Page 100', body='100'), - dict(uid='101', title='Page 101', body='101'), - dict(uid='102', title='Page 102', body='102'), -] - - -class WikiViews(object): - def __init__(self, request): - self.request = request - renderer = get_renderer("templates/layout.pt") - self.layout = renderer.implementation().macros['layout'] - - def get_pages(self): - return pages - - @view_config(route_name='wiki_view', - renderer='templates/wiki_view.pt') - def wiki_view(self): - return dict(title='Welcome to the Wiki', pages=pages) - - @view_config(route_name='wikipage_add', - renderer='templates/wikipage_addedit.pt') - def wikipage_add(self): - return dict(title='Add Wiki Page') - - @view_config(route_name='wikipage_view', - renderer='templates/wikipage_view.pt') - def wikipage_view(self): - uid = self.request.matchdict['uid'] - page = [page for page in pages if page['uid'] == uid][0] - title = page['title'] - return dict(page=page, title=title) - - @view_config(route_name='wikipage_edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_edit(self): - uid = self.request.matchdict['uid'] - page = [page for page in pages if page['uid'] == uid][0] - title = 'Edit ' + page['title'] - return dict(title=title) - - @view_config(route_name='wikipage_delete') - def wikipage_delete(self): - url = self.request.route_url('wiki_view') - return HTTPFound(url) \ No newline at end of file diff --git a/getting_started/09-forms/development.ini b/getting_started/09-forms/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/09-forms/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/09-forms/index.rst b/getting_started/09-forms/index.rst deleted file mode 100644 index 65f8804..0000000 --- a/getting_started/09-forms/index.rst +++ /dev/null @@ -1,162 +0,0 @@ -=================================== -9: Forms and Validation With Deform -=================================== - -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. - -Deform is one such library. In this step, we introduce Deform for our -forms and validation. - -Objectives -========== - -- Make a schema using Colander, the companion to Deform - -- Create a form with Deform and change our views to handle validation - -Steps -===== - - -#. Let's use the previous package as a starting point for a new - distribution. Also, use ``easy_install`` to install Deform: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step08 step09; cd step09 - (env33)$ easy_install-3.3 deform - (env33)$ python3.3 setup.py develop - -#. Deform has CSS and JS that help make it look pretty. Change the - ``tutorial/__init__.py`` to add a static view for Deform's static - assets: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. To keep our dummy data out of our ``views.py`` (and pave the way for - a future step that does modeling), let's move ``pages`` to - ``tutorial/models.py``: - - .. literalinclude:: tutorial/models.py - -#. Our ``tutorial/views.py`` has some significant changes. The add and - edit views handle both GET and POST (form submission), - we have methods, and most of all, a form schema for WikiPage: - - .. literalinclude:: tutorial/views.py - :linenos: - -#. We don't want to include the Deform JS/CSS in every page. We thus need - a "slot" in ``tutorial/templates/layout.pt`` into which we can insert - these static assets: - - .. literalinclude:: tutorial/templates/layout.pt - :linenos: - :language: html - -#. ``tutorial/templates/wikipage_addedit.pt`` needs to iterate over the - resources and insert them in the slot we just made, - as well as insert the rendered form: - - .. literalinclude:: tutorial/templates/wikipage_addedit.pt - :linenos: - :language: html - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -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. - -We just need to include a call to ``add_static_view`` to make that -directory available at a URL. For Pyramid-specific pages, -Pyramid provides a facility (``config.include()``) which even makes -that unnecessary for consumers of a package. (Deform is not specific to -Pyramid.) - -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 "submit". - -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 -````. - -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 POST, validate the form contents - -- If the form is invalid, bail out by re-rendering the form with the - supplied ``POST`` data - -- If the validation succeeeded, 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. - -Extra Credit -============ - -#. Do I have to publish my Deform static assets at the - ``/deform_static/`` URL path? What happens if I change it? (Give - this a try by editing ``deform_static`` in ``tutorial/__init__.py``.) - -#. Analyze the following and discern what is the intention: - - .. code-block:: python - :linenos: - - @view_defaults(route_name='wikipage_edit', - renderer='templates/wikipage_addedit.pt') - class WikiPageViews(object): - - def __init__(self, request): - self.request = request - - @view_config(request_param='form.update') - def wikipage_update(self): - # some work - return dict(title="Form Update") - - @view_config(request_param='form.draft') - def wikipage_draft(self): - # some work - return dict(title="Form Draft") - - @view_config(request_param='form.delete') - def wikipage_delete(self): - # some work - return dict(title="Form Delete") - diff --git a/getting_started/09-forms/setup.py b/getting_started/09-forms/setup.py deleted file mode 100644 index 0b71b73..0000000 --- a/getting_started/09-forms/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon', -] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, -) diff --git a/getting_started/09-forms/tutorial/__init__.py b/getting_started/09-forms/tutorial/__init__.py deleted file mode 100644 index f2ddeb6..0000000 --- a/getting_started/09-forms/tutorial/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -from pyramid.config import Configurator - - -def main(global_config, **settings): - config = Configurator(settings=settings) - config.include('pyramid_chameleon') - config.add_route('wiki_view', '/') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_route('wikipage_delete', '/{uid}/delete') - config.add_static_view(name='static', path='tutorial:static') - config.add_static_view('deform_static', 'deform:static/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/09-forms/tutorial/models.py b/getting_started/09-forms/tutorial/models.py deleted file mode 100644 index fc73327..0000000 --- a/getting_started/09-forms/tutorial/models.py +++ /dev/null @@ -1,5 +0,0 @@ -pages = { - '100': dict(uid='100', title='Page 100', body='100'), - '101': dict(uid='101', title='Page 101', body='101'), - '102': dict(uid='102', title='Page 102', body='102') -} \ No newline at end of file diff --git a/getting_started/09-forms/tutorial/static/logo.png b/getting_started/09-forms/tutorial/static/logo.png deleted file mode 100644 index a5bc0ade71d3da5eab67391e840ff20448c42cd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7044 zcmV-~8++u5P) zdzci}o%cWIR8?Q*J}|@F9B_mg1Byg}C(F!U?yB?tajLqjd%9-|bY7;1uK18*V5JNA0O7?gXZwlyQ=~5JHG!Lh*b|$Y<8P_TL|# z){fhR;BEzDg%BvE2CYB0{SKvgL%|PzyK3dzgVxy)hL8oP zgmBZRUt48`ZSi0(nVmYx~8(+EYEAA$?mjKfCn^$(-Eb>KLf?+7wNzGj$H!W2z&q(0h@sm z;Q3q(EN6rhoyu~8DgY;PJcq#8Z(s?H`3pz*T zpTMB}uz#sO78ArN#2-VSUB-UfaPv<|S3GYJN88*m};B=E-pwi5>Cpr3~#m2mF^ zbAg?}4}e3!h5`0-!pNZw*M;qbvUq7kMxg|*CaCmX>+Fs*U0n|VIg;Wf;=TJnom z3djp76e4+kdRv0zQQ;$^(7TQru!>s%{1B+aKSv7fBtQ7IO8sH-jE{yQgO z(n-8q@b}lZuxVGAKW;Gy2DCvZJVGTH^kg}vuZ9X>I&cl} z8Q>Z8oA;?t(%GjRAzwbo;Ca#i4gK8iM?b5adGT^eIF!ojyhsoPKWRkbv{2CGpYFPd zsxnEevkm1)@&l3wZa&VPOUgLb2Bskr?PS~vO=XXC2$AUHBw!=3IIV0Ua08lT!`=PW zz^;?k>8A{ z+78_LH{+P{*%IQNZJ@e9S;V3R)KxnC<4s)zEECf-F-;50GO;YnO#uP{oex+Rfj}UU zf`K3b%Q{(WII}Gx_%GmlZoV9tc``ctl%gA0fVQ#TIJH9=9OTr70|Udb0GD_=kmA{pALLQ7l)v;h11 z=v%t|sI-16foilQ+6u%noLk}6iK1<#-fS}e_4MbSn7}Bsm$4PCIm9>}R#jv{&Lo_N zgybW6mz-zFX@sR1D=rLB&AXXmcnGDjz$R&n;IgE;%oN zG(vnJNVh(+~_RiHrGC_?D_E{S6h zFa>wd5;I_3Lcok?sOTTWn&V|6U==j)AGxQU1ud@*CLCl1(d+sg1vS#-h zKj*gpQ?w*L?T|ylfixuHkbc4Y$t9#FhxC=jZJ0l^j0t1&iFeyRca;QTqGS;Q2S-Z4 zAf_ZWWrD9=AK+)N1u#vK^oXWx+;X7_7mw$}LG?Fi=>u9Kcz71L7d;v0qNPUqfWqiW z4Db*AO>J1m8 z$pl(8{t|H1cl9mE_+hXQqnzVlw_~@opyIgheYD^c&~3n9Ho(hkYWbgcLgWQO8U~?Ykb;5& zN=r)_F=7N2l@*i?AMO(Gq=W{>p;t*9L9250zznpi2HH7s7fF}2ho$HV97a#Vbi#cJ ztvh$nbNP|qR*9h{N+z=Pg;gXa-UNCQ&TkL>I>T|$()kv5tZxDfiN(5@_xWQKtBpjD z>;?NEDA(U0wE76H?<9tnAuidJ1lxwN1wZ)fJ{Twkkm(h(x**x7=1kYvNV z?pTU?sdGw@Tf$i;B_$;|wvCWF?q<0vUQ0_0?|-nFTd&zoeaQ}@kr;>o5C9QMZA>|+ zc#?2JxT{oL@^#0dt`ru0c{_J5t4AS_0+7j&fNk3}H#f7txtVx;z=ZQps=a6xu>h@t z9YWh&uK;iL(#CQ0DwOX7=Z8X}shypjYXDJHR5UpfiHrj%Dk@su+S(c*NenT4`tKNgF*H!LHBWmy-+jf7)g?TNbf=MZa1BF$mvQGD_~JtT7qpmn5Kzg80^`zhxO~%^XHGY@x`fe z=3W&c77Ks~_BdY%g<%LJaij@$9O4QROCded3Fx%p+OhC=m$&ef)s=*UD%DX#G9QaZ z`|UzjN_nv&&yr-b7;Au7woy<}aCF_eb%kihO5#c%ee}_i88c>t zy1KfktE;O*J7+QxMb4Z#^K!>=ZUh3MP^hb}uI_{S`uaW9)z!z^+uH*xR;;)o7K;TZ zPMp{P%u!0kytcmk-S0LmSg-(q>#nlo? zEiEl)`Hutk6Hh!L@3`X**~>x$KT*>wg-DV#lPc-QDf@A(zK~PG1x$-!!-gTHq@|^W zRjXd(PakZgy**09*%k}`PZYxt#Nz@X6w;6w!T}K_9_!@j@i@(g1-qKdi0*=s`A}I5 zrG*d*XennqE(vYz8NKICM?ws1x z)bu|U6&2B@rlv~(F1X-=KaU?j-a@Nl-uc2b&9Jvfz%UF0Z3m`1Z;VGCdF0%eUV3Ss z<2Z~NGv=cwpL}w~<(FUHiu-!nym_zh042Q#Bh{2ou#)uIks=dZ)Yin!J z_Lp~D0dnHRiA9!WS+;F+?z!iVMte*>(kj57GiMGDJn%s2;lqcis;VmAw{PD}w@x?E z{q)mM7k%X`UomXkrntDc;MlQaRqnCa#TQ>}zWeUG-nDu6AsbFh4<+Q7aG_v`VWq=Z zwQ3bBUwMVrW5-2MzJO5| zU4VH>fE`;m^81||=-&HB!d)M5R(>m^N;L@!^B`b=VZaanayRpyYM}YJiDe`uo0JmU zwyCbFsz%4dd@`~JRN5BHvVy{$@j?jCfiaLcgE?!~talbJTsZ#t@#Doymo6P$UtfRp z(4j*kq?Go&dGp?N+x53C@J%eG#fD9W$+m3SvP&JuAsh}zmM&fT+_Y)aA|weTjvYIe zi2gf{KX6lBLPDQVH$=Z0Yafr5WPwy(MCf< zL!n37y1Kfv&>q()V6RxQ!kjQ+LOc?QP*6}Xxv{ZvMompk1g)ZWqE*^1pu4`le)!g{ zTfH#MX%zs^Dd8fK2v0uwByX-;=dPPHu^q)f-fD18?Q!1veTW^+MKrfnV}#FTM9p}r z&OM*YuR5FRnkowOQ`P>9Cw&(9TRNk#zxe?B_wJ%;_eZ?FYXi}p8z~jr8CkfSG3D^U zbqDy?i?!H}a6P2jJF1lMR8obWB>fGT3fs1AccNq4wzuqQAa7Ua?z`{)@QEj$Xg+e} zNab&S^P6#-Hf=hnloHj|)w^!L{q}A2Tsgtllt*%dVQmq)Vsq#j#@< zKCGbMZHEJJ&d3VR8Cd~SKL^}Oq$9@Gt=st1d+)M*?J7QQTuVSi={E8(q*g73lt`se zPCR{$V1FQ#Qi%!Gn}q2Ps;jG+G-=WwR;^m~rN+j_aoe|V?*f=Qb?SP{vb^zS?VqG{ zO(9$$5Qs@B9e}pBw!)T{7CB~h3HyaxpFLE45E?&HN=A1cm7S`6*`t3wktXMH(?b@~T zygq}$U>-U~=#ecYxwdM8fDAGTb4Ivh&BvUw@*@31td5gdyc|~sjl1*NzQ2hLAN-iY zqNgY?E90!Q&Z42Afrf?#Mvop%O-&7DWo1~FmC~+l+Z;W5gy!Z0?AWn`ZQHi7b?Y|v z?faO+hmX+K-cB?cCuWxtFeR3uHAdlnt)QgBQI0psP8s0QgiuN;5sgL-k`vv77hLzGT@Up(i?mHwuiejyQwW7yCj6=^$)LXX zT;dBCF8sp_FTAk6si|r5{{8#Uoiu6EKh2#x_k}Nf;S1Zky1LYJ&pkKp<(FT+ITnir z3kwUkMIw>W@pxR4B$%fvrw|?e;_);Hp_F2VhYlU42njhh*DNr|Hz9LE`ugmWCnwr$%0 zux(p1aFr5(*|TSV{NRHRZrr?i^JIYf`ug`TyX>+fWL9%UJRY|l$LXto=fMXbd}-di zdE?vL+lRgJ#v5Ne@4WLShr{8vqeqWc#A31Vq)C%j@7c5G>;nf5jE+Pid884pySqDa zU*hpN?lJ$Egl*f7H1^>}p@AfA2p*`LjtH7jr5 zzJ2Glw6ruVS+e9?k3ar+yW=>9ZQCIsL~QQdx!+s2Zrv4;NMv+08uc7Wj&GZu_uHK6 zw3cv6sUUitV4<6e+!RK8_YA-oK79CaKuD=SP)IB+);p;#dp9AzIBq`;$>WWO{S2|Bb;&^9D87d3xz`6m6erSBaujHRaMm< zJ|W=%tEQ%A&*sgWCrc^gx7>2e>-f~A^#1em^17<3s)_doX7 zV~;-k@WVIm-MjZQkw~Phy}iA7`0(MoufF=~)z3cr?Aj}?xMH^BIFyu>93s`Wa)u8d z9vLxWL=zy&%gYbD$5SL+QBhH6b#?WYj*gCk%F4?9Zk?2|NGYkRs@iUvW@lAZmG>!4 zqK*9g{LaeC$}L@8UHO%jl^>Jp_ifA+{0K(yL57K)5WIC2! z$1n^P2m~^YrH?u)8jVWZwsEh=>r0V=6x+9NH&(4$HN3U8)f_u^?4cWOxZxn0K+#w% zW_NUSIHqaF^7HfE1-HaV#s>ZBWkPjL3;3UOWR-BPT4<3}4GYkcEaaB!Z(}r}Y_w78 z?fVmesI9GSwM^@HG#1O3J&4wmKTCL?<0qU0sB_La$GPK zdm~(ebo8{#(xp~56*8x)qzBU_nnx;c>-|25r>Cc1q(?Y!>rasM{CIf)EzI^K`H{MeUPW_oeTv0z_u=^f*85lb_Uf^g7P2u1>nUx_8}s@4fGFiq_t}_~MJ@ z-~RTuZ`!nJ({+F-EG*ph_~VcNoK!pNOhOMelb)pWlg6LYiI$CYr$o9zQfhgm@PA)r z`;Mg>?FvmwKjHkVG*YCSo8mwI@sEG^(#n-@wYRlR%g@(wwoKB^Qv;KBn9Ne1iLCmE zVYSmlrb;(Y^>lx@Xy?wIOqnuuSzTRS2f3FfV(HSQRd?NW*W#|Ou5y69yu3XN7A*Mw zl~-PQ_!NxibU`Mlu3fvVapT7QytA{jX5`3`uP<7(=x4KM&pwmM1}9FqbmC=_ZgKinHwQf=07=4` zB+1sFluouxwws@H{;gcNaN)>*S+wXO%QPEGN=vXzDrE#d^i&J8 zNxF2x^{Hm&f^dT5Pq6u=oRML_zmAgL2nQfvef8CXZ+zn$^IKY4W`;tcVFd*R1Ofr~ z!_=PADVfr^;`*lI0XK2xJoOmD**k4~?zu`we`qBFH&p+S$?ZPv4lK*c(I^n!+ zf7Hv~R+nUTqU1`#dCrq`$(2og%w)6qy^@(omMzGh>D#ev80gJN{}yeCX#abVDLmk9 zdn25ePbXKp-Ii0rd2zVDE`qeurAsLpT}eP0jT7 z`yya^y_9nX(^Dw`?@NKizAxX`evh{L>T3u?I6?Gh@@JD)y@eDq*2!g9z_Zct<-Ky! z^63~-9fvU3=&zcYt-L=Dm_bKxHe9A9M<+s_A)*aoFmNK|=noNr!Ix}_eg<-qYxfOd i2&XM_{d<%B#s3F+!sT<+TNZKv0000 - - - Wiki - ${title} - - - - -
-

- - Logo - ${title}

- -
-
-
- - \ No newline at end of file diff --git a/getting_started/09-forms/tutorial/templates/wiki_view.pt b/getting_started/09-forms/tutorial/templates/wiki_view.pt deleted file mode 100644 index 149d469..0000000 --- a/getting_started/09-forms/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,13 +0,0 @@ - \ No newline at end of file diff --git a/getting_started/09-forms/tutorial/templates/wikipage_addedit.pt b/getting_started/09-forms/tutorial/templates/wikipage_addedit.pt deleted file mode 100644 index 52a147a..0000000 --- a/getting_started/09-forms/tutorial/templates/wikipage_addedit.pt +++ /dev/null @@ -1,18 +0,0 @@ -
- - - - - - - - -
-

${structure: form}

- -
-
\ No newline at end of file diff --git a/getting_started/09-forms/tutorial/templates/wikipage_view.pt b/getting_started/09-forms/tutorial/templates/wikipage_view.pt deleted file mode 100644 index 4cbd946..0000000 --- a/getting_started/09-forms/tutorial/templates/wikipage_view.pt +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - Edit - | - - Delete - - -

${structure: page.body}

-
-
\ No newline at end of file diff --git a/getting_started/09-forms/tutorial/tests.py b/getting_started/09-forms/tutorial/tests.py deleted file mode 100644 index 07e9861..0000000 --- a/getting_started/09-forms/tutorial/tests.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest - -from pyramid import testing - - -class WikiViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_wiki_view(self): - from tutorial.views import WikiViews - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(response['title'], 'Welcome to the Wiki') - - -class WikiFunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - - settings = {} - app = main(settings) - from webtest import TestApp - - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Welcome', res.body) - res = self.testapp.get('/add', status=200) - self.assertIn(b'Add Wiki Page', res.body) - res = self.testapp.get('/100', status=200) - self.assertIn(b'100', res.body) - res = self.testapp.get('/100/edit', status=200) - self.assertIn(b'Edit', res.body) - res = self.testapp.get('/100/delete', status=302) - self.assertIn(b'Found', res.body) diff --git a/getting_started/09-forms/tutorial/views.py b/getting_started/09-forms/tutorial/views.py deleted file mode 100644 index 6b7c3a3..0000000 --- a/getting_started/09-forms/tutorial/views.py +++ /dev/null @@ -1,109 +0,0 @@ -import colander -import deform.widget - -from pyramid.decorator import reify -from pyramid.httpexceptions import HTTPFound -from pyramid.renderers import get_renderer -from pyramid.view import view_config - -from .models import pages - - -class WikiPage(colander.MappingSchema): - title = colander.SchemaNode(colander.String()) - body = colander.SchemaNode( - colander.String(), - widget=deform.widget.RichTextWidget() - ) - - -class WikiViews(object): - def __init__(self, request): - self.request = request - renderer = get_renderer("templates/layout.pt") - self.layout = renderer.implementation().macros['layout'] - - @reify - def wiki_form(self): - schema = WikiPage() - return deform.Form(schema, buttons=('submit',)) - - @reify - def reqts(self): - return self.wiki_form.get_widget_resources() - - @view_config(route_name='wiki_view', - renderer='templates/wiki_view.pt') - def wiki_view(self): - return dict(title='Welcome to the Wiki', - pages=pages.values()) - - @view_config(route_name='wikipage_add', - renderer='templates/wikipage_addedit.pt') - def wikipage_add(self): - form = self.wiki_form.render() - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - # Form is NOT valid - return dict(title='Add Wiki Page', form=e.render()) - - # Form is valid, make a new identifier and add to list - last_uid = int(sorted(pages.keys())[-1]) - new_uid = str(last_uid + 1) - pages[new_uid] = dict( - uid=new_uid, title=appstruct['title'], - body=appstruct['body'] - ) - - # Now visit new page - url = self.request.route_url('wikipage_view', uid=new_uid) - return HTTPFound(url) - - return dict(title='Add Wiki Page', form=form) - - @view_config(route_name='wikipage_view', - renderer='templates/wikipage_view.pt') - def wikipage_view(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - return dict(page=page, title=page['title']) - - @view_config(route_name='wikipage_edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_edit(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - title = 'Edit ' + page['title'] - - wiki_form = self.wiki_form - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = wiki_form.validate(controls) - except deform.ValidationFailure as e: - return dict(title=title, page=page, form=e.render()) - - # Change the content and redirect to the view - page['title'] = appstruct['title'] - page['body'] = appstruct['body'] - - url = self.request.route_url('wikipage_view', - uid=page['uid']) - return HTTPFound(url) - - form = wiki_form.render(page) - - return dict(page=page, title=title, form=form) - - @view_config(route_name='wikipage_delete') - def wikipage_delete(self): - uid = self.request.matchdict['uid'] - del pages[uid] - - url = self.request.route_url('wiki_view') - return HTTPFound(url) \ No newline at end of file diff --git a/getting_started/10-security/development.ini b/getting_started/10-security/development.ini deleted file mode 100644 index 1a84826..0000000 --- a/getting_started/10-security/development.ini +++ /dev/null @@ -1,30 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/10-security/index.rst b/getting_started/10-security/index.rst deleted file mode 100644 index ae6a312..0000000 --- a/getting_started/10-security/index.rst +++ /dev/null @@ -1,140 +0,0 @@ -================================================== -10: Security With Authentication and Authorization -================================================== - -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/delete 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) - -- Create login/logout views - -Steps -===== - -#. Copy the results from the previous step: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step09 step10; cd step10 - (env33)$ python3.3 setup.py develop - -#. Update ``tutorial/models.py`` to include statements from Pyramid's - declarative security features: - - .. literalinclude:: tutorial/models.py - :linenos: - -#. Our ``__init__.py`` needs this root factory, our - authentication/authorization policies, and routes to login/logout: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. Create a ``tutorial/security.py`` module that can find our user - information by providing an *authentication policy callback*: - - .. literalinclude:: tutorial/security.py - :linenos: - -#. Our ``tutorial/views.py`` needs some changes: permissions on the - add/edit/delete views, new views for login/logout, - and a way to track whether a user is ``logged_in``: - - .. literalinclude:: tutorial/views.py - :linenos: - -#. We have a login view that needs a template at - ``tutorial/templates/login.pt``: - - .. literalinclude:: tutorial/templates/login.pt - :linenos: - :language: html - -#. ``tutorial/templates/layout.pt`` needs a conditional link to appear - on all pages, for either logging in or logging out: - - .. literalinclude:: tutorial/templates/layout.pt - :linenos: - :language: html - -#. Let's style that link by adding a float to - ``tutorial/static/wiki.css``: - - .. literalinclude:: tutorial/static/wiki.css - :language: css - -#. Finally, we need to change our functional tests, as requests to - add/edit/delete get redirected to the login screen: - - .. literalinclude:: tutorial/tests.py - :linenos: - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your browser. - -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. - -This simple tutorial step can be boiled down to the following: - -- A view can require a *permission* (``edit``) - -- 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* - -- The registered ``groupfinder`` answers whether a particular user - (``editor``) has a particular group (``group:editors``) - -In summary: ``wikipage_add`` 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. - -If you are not logged in and click on ``Add WikiPage``, you need to get -sent to a 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 -============ - -#. Can I use a database behind my ``groupfinder`` to look up principals? - -#. Do I have to put a ``renderer`` in my ``@forbidden_view_config`` - decorator? - -#. Once I am logged in, does any user-centric information get jammed - onto each request? Use ``import pdb; pdb.set_trace()`` to answer - this. diff --git a/getting_started/10-security/setup.py b/getting_started/10-security/setup.py deleted file mode 100644 index 0b71b73..0000000 --- a/getting_started/10-security/setup.py +++ /dev/null @@ -1,14 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon', -] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - """, -) diff --git a/getting_started/10-security/tutorial/__init__.py b/getting_started/10-security/tutorial/__init__.py deleted file mode 100644 index b5a8ad0..0000000 --- a/getting_started/10-security/tutorial/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.config import Configurator - -from .security import groupfinder - -def main(global_config, **settings): - config = Configurator(settings=settings, - root_factory='tutorial.models.Root') - config.include('pyramid_chameleon') - - # Security policies - authn_policy = AuthTktAuthenticationPolicy( - 'sosecret', callback=groupfinder, hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) - - config.add_route('wiki_view', '/') - config.add_route('login', '/login') - config.add_route('logout', '/logout') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_route('wikipage_delete', '/{uid}/delete') - config.add_static_view(name='static', path='tutorial:static') - config.add_static_view('deform_static', 'deform:static/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/10-security/tutorial/models.py b/getting_started/10-security/tutorial/models.py deleted file mode 100644 index d138243..0000000 --- a/getting_started/10-security/tutorial/models.py +++ /dev/null @@ -1,15 +0,0 @@ -from pyramid.security import Allow, Everyone - -pages = { - '100': dict(uid='100', title='Page 100', body='100'), - '101': dict(uid='101', title='Page 101', body='101'), - '102': dict(uid='102', title='Page 102', body='102') -} - - -class Root(object): - __acl__ = [(Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit')] - - def __init__(self, request): - pass \ No newline at end of file diff --git a/getting_started/10-security/tutorial/security.py b/getting_started/10-security/tutorial/security.py deleted file mode 100644 index ab90bab..0000000 --- a/getting_started/10-security/tutorial/security.py +++ /dev/null @@ -1,8 +0,0 @@ -USERS = {'editor': 'editor', - 'viewer': 'viewer'} -GROUPS = {'editor': ['group:editors']} - - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) \ No newline at end of file diff --git a/getting_started/10-security/tutorial/static/logo.png b/getting_started/10-security/tutorial/static/logo.png deleted file mode 100644 index a5bc0ade71d3da5eab67391e840ff20448c42cd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7044 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 i2&XM_{d<%B#s3F+!sT<+TNZKv0000 - - - Wiki - ${title} - - - - -
-
- Logout - Log In -
-

- - Logo - ${title}

- -
-
-
- - \ No newline at end of file diff --git a/getting_started/10-security/tutorial/templates/login.pt b/getting_started/10-security/tutorial/templates/login.pt deleted file mode 100644 index fa08423..0000000 --- a/getting_started/10-security/tutorial/templates/login.pt +++ /dev/null @@ -1,21 +0,0 @@ -
-
-

Login

- - -
- - -
- -
- -
-
-
\ No newline at end of file diff --git a/getting_started/10-security/tutorial/templates/wiki_view.pt b/getting_started/10-security/tutorial/templates/wiki_view.pt deleted file mode 100644 index 149d469..0000000 --- a/getting_started/10-security/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,13 +0,0 @@ - \ No newline at end of file diff --git a/getting_started/10-security/tutorial/templates/wikipage_addedit.pt b/getting_started/10-security/tutorial/templates/wikipage_addedit.pt deleted file mode 100644 index 52a147a..0000000 --- a/getting_started/10-security/tutorial/templates/wikipage_addedit.pt +++ /dev/null @@ -1,18 +0,0 @@ -
- - - - - - - - -
-

${structure: form}

- -
-
\ No newline at end of file diff --git a/getting_started/10-security/tutorial/templates/wikipage_view.pt b/getting_started/10-security/tutorial/templates/wikipage_view.pt deleted file mode 100644 index 4cbd946..0000000 --- a/getting_started/10-security/tutorial/templates/wikipage_view.pt +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - Edit - | - - Delete - - -

${structure: page.body}

-
-
\ No newline at end of file diff --git a/getting_started/10-security/tutorial/tests.py b/getting_started/10-security/tutorial/tests.py deleted file mode 100644 index 1896c1d..0000000 --- a/getting_started/10-security/tutorial/tests.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest - -from pyramid import testing - - -class WikiViewTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def test_wiki_view(self): - from tutorial.views import WikiViews - self.config.include('pyramid_chameleon') - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(response['title'], 'Welcome to the Wiki') - - -class WikiFunctionalTests(unittest.TestCase): - def setUp(self): - from tutorial import main - - settings = {} - app = main(settings) - from webtest import TestApp - - self.testapp = TestApp(app) - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Welcome', res.body) - res = self.testapp.get('/add', status=200) - self.assertIn(b'Log', res.body) - res = self.testapp.get('/100', status=200) - self.assertIn(b'100', res.body) - res = self.testapp.get('/100/edit', status=200) - self.assertIn(b'Log', res.body) - res = self.testapp.get('/100/delete', status=200) - self.assertIn(b'Log', res.body) diff --git a/getting_started/10-security/tutorial/views.py b/getting_started/10-security/tutorial/views.py deleted file mode 100644 index 4a39deb..0000000 --- a/getting_started/10-security/tutorial/views.py +++ /dev/null @@ -1,149 +0,0 @@ -import colander -import deform.widget - -from pyramid.decorator import reify -from pyramid.httpexceptions import HTTPFound -from pyramid.renderers import get_renderer -from pyramid.security import remember, forget, authenticated_userid -from pyramid.view import view_config, forbidden_view_config - -from .models import pages -from .security import USERS - - -class WikiPage(colander.MappingSchema): - title = colander.SchemaNode(colander.String()) - body = colander.SchemaNode( - colander.String(), - widget=deform.widget.RichTextWidget() - ) - - -class WikiViews(object): - def __init__(self, request): - self.request = request - renderer = get_renderer("templates/layout.pt") - self.layout = renderer.implementation().macros['layout'] - self.logged_in = authenticated_userid(request) - - @reify - def wiki_form(self): - schema = WikiPage() - return deform.Form(schema, buttons=('submit',)) - - @reify - def reqts(self): - return self.wiki_form.get_widget_resources() - - @view_config(route_name='wiki_view', - renderer='templates/wiki_view.pt') - def wiki_view(self): - return dict(title='Welcome to the Wiki', - pages=pages.values()) - - @view_config(route_name='wikipage_add', - permission='edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_add(self): - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - # Form is NOT valid - return dict(title='Add Wiki Page', form=e.render()) - - # Form is valid, make a new identifier and add to list - last_uid = int(sorted(pages.keys())[-1]) - new_uid = str(last_uid + 1) - pages[new_uid] = dict( - uid=new_uid, title=appstruct['title'], - body=appstruct['body'] - ) - - # Now visit new page - url = self.request.route_url('wikipage_view', uid=new_uid) - return HTTPFound(url) - - return dict(title='Add Wiki Page', form=self.wiki_form.render()) - - @view_config(route_name='wikipage_view', - renderer='templates/wikipage_view.pt') - def wikipage_view(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - return dict(page=page, title=page['title']) - - @view_config(route_name='wikipage_edit', - permission='edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_edit(self): - uid = self.request.matchdict['uid'] - page = pages[uid] - title = 'Edit ' + page['title'] - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - return dict(title=title, page=page, form=e.render()) - - # Change the content and redirect to the view - page['title'] = appstruct['title'] - page['body'] = appstruct['body'] - - url = self.request.route_url('wikipage_view', - uid=page['uid']) - return HTTPFound(url) - - form = self.wiki_form.render(page) - - return dict(page=page, title=title, form=form) - - @view_config(route_name='wikipage_delete', permission='edit') - def wikipage_delete(self): - uid = self.request.matchdict['uid'] - del pages[uid] - - url = self.request.route_url('wiki_view') - return HTTPFound(url) - - @view_config(route_name='login', renderer='templates/login.pt') - @forbidden_view_config(renderer='templates/login.pt') - def login(self): - request = self.request - login_url = request.route_url('login') - referrer = request.referer - if referrer == login_url: - referrer = '/' # never use login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, - headers=headers) - message = 'Failed login' - - return dict( - title='Login', - message=message, - url=request.application_url + '/login', - came_from=came_from, - login=login, - password=password, - ) - - @view_config(route_name='logout') - def logout(self): - request = self.request - headers = forget(request) - url = request.route_url('wiki_view') - return HTTPFound(location=url, - headers=headers) diff --git a/getting_started/11-sqlalchemy/development.ini b/getting_started/11-sqlalchemy/development.ini deleted file mode 100644 index cbb16d8..0000000 --- a/getting_started/11-sqlalchemy/development.ini +++ /dev/null @@ -1,40 +0,0 @@ -[app:main] -use = egg:tutorial -pyramid.reload_templates = true -pyramid.includes = - pyramid_tm - -sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite - - -[server:main] -use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6547 - -[loggers] -keys = root, sqlalchemy - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = INFO -handlers = console - -[logger_sqlalchemy] -level = INFO -handlers = -qualname = sqlalchemy.engine - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s diff --git a/getting_started/11-sqlalchemy/index.rst b/getting_started/11-sqlalchemy/index.rst deleted file mode 100644 index d2a345b..0000000 --- a/getting_started/11-sqlalchemy/index.rst +++ /dev/null @@ -1,206 +0,0 @@ -============================== -11: Databases Using SQLAlchemy -============================== - -Our Pyramid-based wiki application now needs database-backed storage of -pages. This frequently means a SQL database. The Pyramid community -strongly supports the SQLAlchemy object-relational mapper (ORM) as a -convenient, Pythonic way to interface to databases. - -In this step we hook up SQLAlchemy to a SQLite database table. - -Objectives -========== - -- Store pages in SQLite by using SQLAlchemy models - -- Use SQLAlchemy queries to list/add/view/edit/delete pages - -- Provide a database-initialize command by writing a Pyramid *console - script* which can be run from the command line - -.. 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. - -Steps To Initialize Database -============================ - -.. warning:: - - To make sure that your Python can reach your SQLite library - correctly, please make sure the following succeeds: - - .. code-block:: bash - - $ python3.3 - Python 3.3.0 (default, Mar 8 2013, 15:50:47) - [GCC 4.2.1 Compatible Apple Clang 4.0 ((tags/Apple/clang-421.0.60))] on darwin - Type "help", "copyright", "credits" or "license" for more information. - >>> from sqlite3 import * - >>> - -#. As before, let's use the previous package as a starting point for - a new distribution. Also, let's install the dependencies required - for a SQLAlchemy-oriented Pyramid application and make a directory - for the console script: - - .. code-block:: bash - - (env33)$ cd ..; cp -r step10 step11; cd step11 - (env33)$ easy_install-3.3 sqlalchemy pyramid_tm zope.sqlalchemy - - .. note:: - - We aren't yet doing ``python3.3 setup.py develop`` as we - are changing it later. - -#. Our configuration file at ``development.ini`` wires together some - new pieces: - - .. literalinclude:: development.ini - :language: ini - -#. This engine configuration now needs to be read into the application - through changes in ``tutorial/__init__.py``: - - .. literalinclude:: tutorial/__init__.py - :linenos: - -#. We need a command-line script for initializing the database. Enter - the following to initialize ``tutorial/scripts/__init__.py``: - - .. literalinclude:: tutorial/scripts/__init__.py - -#. Now enter our console script at - ``tutorial/scripts/initializedb.py``: - - .. literalinclude:: tutorial/scripts/initializedb.py - -#. To wire up this new console script, our ``setup.py`` needs an entry - point: - - .. literalinclude:: setup.py - -#. Since ``setup.py`` changed, we now run it: - - .. code-block:: bash - - (env33)$ python3.3 setup.py develop - -#. The script references some models in ``tutorial/models.py``: - - .. literalinclude:: tutorial/models.py - :linenos: - -#. Let's run this console script, thus producing our database and table: - - .. code-block:: bash - - (env33)$ initialize_tutorial_db development.ini - 2013-03-12 10:13:56,972 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages") - 2013-03-12 10:13:56,972 INFO [sqlalchemy.engine.base.Engine][MainThread] () - 2013-03-12 10:13:56,974 INFO [sqlalchemy.engine.base.Engine][MainThread] - CREATE TABLE wikipages ( - id INTEGER NOT NULL, - title TEXT, - body TEXT, - PRIMARY KEY (id), - UNIQUE (title) - ) - - - 2013-03-12 10:13:56,974 INFO [sqlalchemy.engine.base.Engine][MainThread] () - 2013-03-12 10:13:56,977 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT - 2013-03-12 10:13:56,981 INFO [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit) - 2013-03-12 10:13:56,983 INFO [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO wikipages (title, body) VALUES (?, ?) - 2013-03-12 10:13:56,983 INFO [sqlalchemy.engine.base.Engine][MainThread] ('Root', '

Root

') - 2013-03-12 10:13:56,985 INFO [sqlalchemy.engine.base.Engine][MainThread] COMMIT - -Application Steps -================= - -#. With our data now driven by SQLAlchemy queries, - we need to update our ``tutorial/views.py``: - - .. literalinclude:: tutorial/views.py - -#. The introduction of a relational database means significant changes - in our ``tutorial/tests.py``: - - .. literalinclude:: tutorial/tests.py - -#. Run the tests in your package using ``nose``: - - .. code-block:: bash - - (env33)$ nosetests . - .. - ----------------------------------------------------------------- - Ran 2 tests in 1.971s - - OK - -#. Run the WSGI application: - - .. code-block:: bash - - (env33)$ pserve development.ini --reload - -#. Open ``http://127.0.0.1:6547/`` in your 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? - -Pyramid has a strong orientation towards support for ``transactions``. -Specifically, you can install a transaction manager into your app -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. - -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. - -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. - -The ``initializedb`` is a nice example of framework support. You point -your setup at the location of some ``[console_scripts]`` and these get -generated into your virtualenv'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``. - -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. - -Extra Credit -============ - -#. Why all this code? Why can't I just type 2 lines have magic ensue? \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/setup.py b/getting_started/11-sqlalchemy/setup.py deleted file mode 100644 index 499cca3..0000000 --- a/getting_started/11-sqlalchemy/setup.py +++ /dev/null @@ -1,16 +0,0 @@ -from setuptools import setup - -requires = [ - 'pyramid', - 'pyramid_chameleon', -] - -setup(name='tutorial', - install_requires=requires, - entry_points="""\ - [paste.app_factory] - main = tutorial:main - [console_scripts] - initialize_tutorial_db = tutorial.scripts.initializedb:main - """, -) \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/sqltutorial.sqlite b/getting_started/11-sqlalchemy/sqltutorial.sqlite deleted file mode 100644 index 4aff655ce6ac199a8b867126cf6ca98ff7684696..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI$ze~eF6bJCTYp5hp&JI#K<$(?@C{m=G6ssIkjM2sv>{L>kO0b&#NU@In^ZXB- z-JIN<9K2MTwX4ea!9Cu4xr5}hU2fV=l!P>lrw>6wM{JvM&Q6FJV}@Qiz1G2XkNrL_; ze2VU)ac~#TvO=7Bs_858o2QPVY)lo)oJRv{xxPAA9=TUOxgE#ZC(BHtWE7IGZvC|D z$Ajmrx%OI@O|MHAs!J7pzMAgnxUF_aQDt?mYE^3@Us5(_(c>Uo%qAm!tM3K#BwqEs z>=k;otW59rMnNYbUzvQ@2>}5JKmY;|fB*y_009U<00Izzz@HM3!eB)^7#@!sIvtK1 zJKSL9T`!K4#X=9$A{|@$tAzYu@>3@S1Rwwb2tWV=5P$##AOHafKmY>&K_H(i@?a>; KT-p5j74Qv}WLen& diff --git a/getting_started/11-sqlalchemy/tutorial/__init__.py b/getting_started/11-sqlalchemy/tutorial/__init__.py deleted file mode 100644 index c79529c..0000000 --- a/getting_started/11-sqlalchemy/tutorial/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy -from pyramid.config import Configurator - -from sqlalchemy import engine_from_config - -from .models import DBSession, Base -from .security import groupfinder - - -def main(global_config, **settings): - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine - config = Configurator(settings=settings, - root_factory='tutorial.models.Root') - config.include('pyramid_chameleon') - - # Security policies - authn_policy = AuthTktAuthenticationPolicy( - 'sosecret', callback=groupfinder, hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) - - config.add_route('wiki_view', '/') - config.add_route('login', '/login') - config.add_route('logout', '/logout') - config.add_route('wikipage_add', '/add') - config.add_route('wikipage_view', '/{uid}') - config.add_route('wikipage_edit', '/{uid}/edit') - config.add_route('wikipage_delete', '/{uid}/delete') - config.add_static_view(name='static', path='tutorial:static') - config.add_static_view('deform_static', 'deform:static/') - config.scan() - return config.make_wsgi_app() diff --git a/getting_started/11-sqlalchemy/tutorial/models.py b/getting_started/11-sqlalchemy/tutorial/models.py deleted file mode 100644 index 5c433ae..0000000 --- a/getting_started/11-sqlalchemy/tutorial/models.py +++ /dev/null @@ -1,39 +0,0 @@ -from pyramid.security import Allow, Everyone - -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session( - sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class Page(Base): - __tablename__ = 'wikipages' - uid = Column(Integer, primary_key=True) - title = Column(Text, unique=True) - body = Column(Text) - - def __init__(self, title, body): - self.title = title - self.body = body - - -class Root(object): - __acl__ = [(Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit')] - - def __init__(self, request): - pass \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/scripts/__init__.py b/getting_started/11-sqlalchemy/tutorial/scripts/__init__.py deleted file mode 100644 index 5bb534f..0000000 --- a/getting_started/11-sqlalchemy/tutorial/scripts/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package diff --git a/getting_started/11-sqlalchemy/tutorial/scripts/initializedb.py b/getting_started/11-sqlalchemy/tutorial/scripts/initializedb.py deleted file mode 100644 index 15c2120..0000000 --- a/getting_started/11-sqlalchemy/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,37 +0,0 @@ -import os -import sys -import transaction - -from sqlalchemy import engine_from_config - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from ..models import ( - DBSession, - Page, - Base, - ) - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s \n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) != 2: - usage(argv) - config_uri = argv[1] - setup_logging(config_uri) - settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.create_all(engine) - with transaction.manager: - model = Page(title='Root', body='

Root

') - DBSession.add(model) diff --git a/getting_started/11-sqlalchemy/tutorial/security.py b/getting_started/11-sqlalchemy/tutorial/security.py deleted file mode 100644 index ab90bab..0000000 --- a/getting_started/11-sqlalchemy/tutorial/security.py +++ /dev/null @@ -1,8 +0,0 @@ -USERS = {'editor': 'editor', - 'viewer': 'viewer'} -GROUPS = {'editor': ['group:editors']} - - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/static/logo.png b/getting_started/11-sqlalchemy/tutorial/static/logo.png deleted file mode 100644 index a5bc0ade71d3da5eab67391e840ff20448c42cd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7044 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 i2&XM_{d<%B#s3F+!sT<+TNZKv0000 - - - Wiki - ${title} - - - - -
-
- Logout - Log In -

- - Logo - ${title}

- -
-
-
- - \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/templates/login.pt b/getting_started/11-sqlalchemy/tutorial/templates/login.pt deleted file mode 100644 index fa08423..0000000 --- a/getting_started/11-sqlalchemy/tutorial/templates/login.pt +++ /dev/null @@ -1,21 +0,0 @@ -
-
-

Login

- - -
- - -
- -
- -
-
-
\ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/templates/wiki_view.pt b/getting_started/11-sqlalchemy/tutorial/templates/wiki_view.pt deleted file mode 100644 index 149d469..0000000 --- a/getting_started/11-sqlalchemy/tutorial/templates/wiki_view.pt +++ /dev/null @@ -1,13 +0,0 @@ - \ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/templates/wikipage_addedit.pt b/getting_started/11-sqlalchemy/tutorial/templates/wikipage_addedit.pt deleted file mode 100644 index 52a147a..0000000 --- a/getting_started/11-sqlalchemy/tutorial/templates/wikipage_addedit.pt +++ /dev/null @@ -1,18 +0,0 @@ -
- - - - - - - - -
-

${structure: form}

- -
-
\ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/templates/wikipage_view.pt b/getting_started/11-sqlalchemy/tutorial/templates/wikipage_view.pt deleted file mode 100644 index 4cbd946..0000000 --- a/getting_started/11-sqlalchemy/tutorial/templates/wikipage_view.pt +++ /dev/null @@ -1,12 +0,0 @@ -
-
- - Edit - | - - Delete - - -

${structure: page.body}

-
-
\ No newline at end of file diff --git a/getting_started/11-sqlalchemy/tutorial/tests.py b/getting_started/11-sqlalchemy/tutorial/tests.py deleted file mode 100644 index 233f05a..0000000 --- a/getting_started/11-sqlalchemy/tutorial/tests.py +++ /dev/null @@ -1,59 +0,0 @@ -import unittest -import transaction - -from pyramid import testing - - -def _initTestingDB(): - from sqlalchemy import create_engine - from tutorial.models import ( - DBSession, - Page, - Base - ) - engine = create_engine('sqlite://') - Base.metadata.create_all(engine) - DBSession.configure(bind=engine) - with transaction.manager: - model = Page('FrontPage', 'This is the front page') - DBSession.add(model) - return DBSession - - -class WikiViewTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() - - def tearDown(self): - self.session.remove() - testing.tearDown() - - def test_wiki_view(self): - from tutorial.views import WikiViews - self.config.include('pyramid_chameleon') - - request = testing.DummyRequest() - inst = WikiViews(request) - response = inst.wiki_view() - self.assertEqual(response['title'], 'Welcome to the Wiki') - - -class WikiFunctionalTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() - from pyramid.paster import get_app - app = get_app('development.ini') - from webtest import TestApp - self.testapp = TestApp(app) - - def tearDown(self): - self.session.remove() - testing.tearDown() - - def test_it(self): - res = self.testapp.get('/', status=200) - self.assertIn(b'Welcome', res.body) - res = self.testapp.get('/add', status=200) - self.assertIn(b'Log', res.body) diff --git a/getting_started/11-sqlalchemy/tutorial/views.py b/getting_started/11-sqlalchemy/tutorial/views.py deleted file mode 100644 index 308202c..0000000 --- a/getting_started/11-sqlalchemy/tutorial/views.py +++ /dev/null @@ -1,152 +0,0 @@ -import colander -import deform.widget - -from pyramid.decorator import reify -from pyramid.httpexceptions import HTTPFound -from pyramid.renderers import get_renderer -from pyramid.security import remember, forget, authenticated_userid -from pyramid.view import view_config, forbidden_view_config - -from .models import DBSession, Page -from .security import USERS - - -class WikiPage(colander.MappingSchema): - title = colander.SchemaNode(colander.String()) - body = colander.SchemaNode( - colander.String(), - widget=deform.widget.RichTextWidget() - ) - - -class WikiViews(object): - def __init__(self, request): - self.request = request - renderer = get_renderer("templates/layout.pt") - self.layout = renderer.implementation().macros['layout'] - self.logged_in = authenticated_userid(request) - - @reify - def wiki_form(self): - schema = WikiPage() - return deform.Form(schema, buttons=('submit',)) - - @reify - def reqts(self): - return self.wiki_form.get_widget_resources() - - @view_config(route_name='wiki_view', - renderer='templates/wiki_view.pt') - def wiki_view(self): - pages = DBSession.query(Page).order_by(Page.title) - return dict(title='Welcome to the Wiki', pages=pages) - - @view_config(route_name='wikipage_add', - permission='edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_add(self): - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - # Form is NOT valid - return dict(title='Add Wiki Page', form=e.render()) - - # Add a new page to the database - new_title = appstruct['title'] - new_body = appstruct['body'] - DBSession.add(Page(new_title, new_body)) - - # Get the new ID and redirect - page = DBSession.query(Page).filter_by(title=new_title).one() - new_uid = page.uid - - url = self.request.route_url('wikipage_view', uid=new_uid) - return HTTPFound(url) - - return dict(title='Add Wiki Page', form=self.wiki_form.render()) - - @view_config(route_name='wikipage_view', - renderer='templates/wikipage_view.pt') - def wikipage_view(self): - uid = int(self.request.matchdict['uid']) - page = DBSession.query(Page).filter_by(uid=uid).one() - - return dict(page=page, title=page.title) - - @view_config(route_name='wikipage_edit', - permission='edit', - renderer='templates/wikipage_addedit.pt') - def wikipage_edit(self): - uid = int(self.request.matchdict['uid']) - page = DBSession.query(Page).filter_by(uid=uid).one() - title = 'Edit ' + page.title - - if 'submit' in self.request.params: - controls = self.request.POST.items() - try: - appstruct = self.wiki_form.validate(controls) - except deform.ValidationFailure as e: - return dict(title=title, page=page, form=e.render()) - - # Change the content and redirect to the view - page.title = appstruct['title'] - page.body = appstruct['body'] - - url = self.request.route_url('wikipage_view', uid=uid) - return HTTPFound(url) - - form = self.wiki_form.render(dict( - uid=page.uid, title=page.title, body=page.body) - ) - - return dict(page=page, title=title, form=form) - - @view_config(route_name='wikipage_delete', permission='edit') - def wikipage_delete(self): - uid = int(self.request.matchdict['uid']) - page = DBSession.query(Page).filter_by(uid=uid).one() - DBSession.delete(page) - - url = self.request.route_url('wiki_view') - return HTTPFound(url) - - @view_config(route_name='login', renderer='templates/login.pt') - @forbidden_view_config(renderer='templates/login.pt') - def login(self): - request = self.request - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, - headers=headers) - message = 'Failed login' - - return dict( - title='Login', - message=message, - url=request.application_url + '/login', - came_from=came_from, - login=login, - password=password, - ) - - @view_config(route_name='logout') - def logout(self): - request = self.request - headers = forget(request) - url = request.route_url('wiki_view') - return HTTPFound(location=url, - headers=headers) \ No newline at end of file diff --git a/getting_started/index.rst b/getting_started/index.rst index 0aa3051..13cd832 100644 --- a/getting_started/index.rst +++ b/getting_started/index.rst @@ -2,27 +2,5 @@ Getting Started with Pyramid ============================ -Pyramid is a web framework for Python 2 and 3. This tutorial gives a -Python 2/3-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. - -.. toctree:: - :maxdepth: 1 - - python_setup - pyramid_setup - tutorial_approach - 01-helloworld/index - 02-package/index - 03-config/index - 04-scaffold/index - 05-tests/index - 06-template/index - 07-viewclasses/index - 08-static/index - 09-forms/index - 10-security/index - 11-sqlalchemy/index \ No newline at end of file +This tutorial has been incorporated into the official Pyramid documentation as +:ref:`Quick Tutorial for Pyramid `. diff --git a/getting_started/pyramid_setup.rst b/getting_started/pyramid_setup.rst deleted file mode 100644 index 960efd7..0000000 --- a/getting_started/pyramid_setup.rst +++ /dev/null @@ -1,27 +0,0 @@ -============= -Pyramid Setup -============= - -Installing Pyramid is easy and normal from a Python packaging -perspective. Again, *make sure* you have your virtual environment first -in your path using ``source bin/activate``. - -.. code-block:: bash - - (env33)$ easy_install-3.3 pyramid - ....chuggalugga... - (env33)$ which pserve - -You now have Pyramid installed. The second command confirms this by -looking for the Pyramid ``pserve`` command that should be in the -``bin`` of your virtual environment. - -Installing Everything -===================== - -Later parts of the tutorial install more packages. Most likely, -you'd like to go ahead and get all of it now: - -.. code-block:: bash - - (env33)$ easy_install-3.3 pyramid nose webtest deform sqlalchemy pyramid_chameleon pyramid_tm zope.sqlalchemy diff --git a/getting_started/python_setup.rst b/getting_started/python_setup.rst deleted file mode 100644 index 1313072..0000000 --- a/getting_started/python_setup.rst +++ /dev/null @@ -1,78 +0,0 @@ -============ -Python Setup -============ - -As the world of Python starts to get more traction with Python 3, -important technologies such as Pyramid need to work harder to not just -tolerate Python 3 but to embrace it. As a step towards this, -our *Getting Started* tutorial features Python 3.3+ (though it will -also work as-is in Python 2.) - -This, of course, introduces some complexity. - -Python 3.3 From Source -====================== - -Compiling and installing your own Python 3 or 2 is always the preferred -solution. As this document later shows, issues arise from using -pre-packaged installations. - -First: - - #. Download and compile Python from source - #. Install it to a directory of your choosing - -Below, ``path/to/python/bin`` refers to this directory. - -Making a Virtual Environment -============================ - -Developing in isolation helps us ensure what we are learning doesn't -conflict with any packages installed from other work on the machine. -*Virtual environments* let us do just this. - -Python 3.3 has support for virtual environments using the ``pyvenv`` -command. Since we are embracing Python 3.3, let's use its virtualenv -approach. - -.. code-block:: bash - - $ path/to/python/bin/pyvenv-3.3 env33 - $ source env33/bin/activate - (env33)$ which python3.3 - -Once you do this, your path will be setup to point at the ``bin`` of -your virtual environment. Your prompt will also change, as noted above. - -.. note:: - - If you are an OS X user that got your Python 3.3 from Homebrew, - its pyvenv is broken (as of Feb 2013.) If you still want to use - Homebrew, do ``pip install virtualenv`` and make your virtual - environments with ``virtualenv``. - -.. note:: - - If you are a Windows user that got your Python 3.3 from python.org, - there's no ``pyvenv-3.3`` script and there's no .exe wrapper for - ``pyvenv`` script. Instead of using ``pyvenv`` directly you should either use - ``path\to\python\python.exe path\to\python\Tools\Scripts\pyvenv.py`` - or ``path\to\python\python.exe -m venv`` instead. - See http://stackoverflow.com/q/15981111/95735 for more details. - - -Installing Packaging Tools -========================== - -Almost there. ``pyvenv`` doesn't copy any of the Python packaging tools -into your virtualenv. Fortunately this is easy and supported: just -download and install ``distribute``. - -.. code-block:: bash - - (env33)$ curl -O http://python-distribute.org/distribute_setup.py - (env33)$ python3.3 distribute_setup.py - -.. note:: - - Make sure the ``python3.3`` above is from your virtualenv! diff --git a/getting_started/tutorial_approach.rst b/getting_started/tutorial_approach.rst deleted file mode 100644 index 69d60ca..0000000 --- a/getting_started/tutorial_approach.rst +++ /dev/null @@ -1,76 +0,0 @@ -================= -Tutorial Approach -================= - -- Tutorial broken into topics with quick working examples - -- Each step is a Python *package* with working code in the repo - -- Setup each step with ``python3.3 setup.py develop`` - -This "Getting Started" tutorial is broken into independent steps, -starting with the smallest possible "single file WSGI app" example. -Each of these steps introduces a topic and a very small set of concepts -via working code. Each step corresponds to a directory in this -repo, where each step/topic/directory is a Python package. -To successfully run each step, for example only (do not run this code now):: - - $ cd 02-hellopackage - $ python3.3 setup.py develop - -...and repeat for each step you would like to work on. - -.. note:: - - The first step in the tutorial, as it explains on its page, - does *not* require package installation. - -Scenario and Technologies -========================= - -This tutorial will demonstrate creation of a simple Wiki application -for Pyramid and Python 3.3. As shown in the table of contents, -we will show various Pyramid facilities, such as views and security. We -will also use some important add-on packages (SQLAlchemy for storage, -Deform for forms.) - -.. note:: - - Wiki afficianods will note that this isn't a classic wiki, - with wikiwords that prompt for links stored by wikiname. Instead, - this is more like a classic add/view/edit/delete of pages. - -Directory Tree -============== - -As we develop our tutorial our directory tree will resemble the -structure below:: - - getting_started/ - 01/ - helloworld.py - 02/ - setup.py - tutorial/ - __init__.py - helloworld.py - views.py - 03/ - setup.py - development.ini - tutorial/ - __init__.py - views.py - -Each of the 02-XX directories are a *Python distribution*. (01 is a -single-file application.) At the end of each step, -we copy the old directory into a new directory to use as a starting -point. - -Source Code -=========== - -The source for the code, as well as the documents for this tutorial, -is available in the -`GitHub repo `_ -for ``pyramid_tutorials``.