Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

merge master to feature.introspection

  • Loading branch information...
commit dafb0b3271f525fb8552d155a4dd840ced1656c8 2 parents 8786a57 + 14e5fa9
@mcdonc mcdonc authored
Showing with 1,875 additions and 1,187 deletions.
  1. +0 −3  .gitmodules
  2. +16 −1 CHANGES.txt
  3. +1 −0  docs/.gitignore
  4. +3 −5 docs/Makefile
  5. +0 −1  docs/_themes
  6. +4 −7 docs/api/paster.rst
  7. +22 −7 docs/conf.py
  8. +5 −0 docs/glossary.rst
  9. +8 −7 docs/narr/MyProject/myproject/static/pylons.css
  10. +23 −53 docs/narr/MyProject/myproject/templates/mytemplate.pt
  11. +23 −14 docs/tutorials/wiki/authorization.rst
  12. +2 −2 docs/tutorials/wiki/basiclayout.rst
  13. +0 −43 docs/tutorials/wiki/src/authorization/tutorial/login.py
  14. +44 −1 docs/tutorials/wiki/src/authorization/tutorial/views.py
  15. +0 −43 docs/tutorials/wiki/src/tests/tutorial/login.py
  16. +44 −1 docs/tutorials/wiki/src/tests/tutorial/views.py
  17. +3 −4 docs/tutorials/wiki/tests.rst
  18. +78 −75 docs/tutorials/wiki2/authorization.rst
  19. +109 −86 docs/tutorials/wiki2/basiclayout.rst
  20. +70 −33 docs/tutorials/wiki2/definingmodels.rst
  21. +81 −80 docs/tutorials/wiki2/definingviews.rst
  22. +127 −21 docs/tutorials/wiki2/installation.rst
  23. +0 −3  docs/tutorials/wiki2/src/authorization/README.txt
  24. +7 −1 docs/tutorials/wiki2/src/authorization/development.ini
  25. +1 −0  docs/tutorials/wiki2/src/authorization/production.ini
  26. +2 −1  docs/tutorials/wiki2/src/authorization/setup.py
  27. +6 −19 docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
  28. +0 −37 docs/tutorials/wiki2/src/authorization/tutorial/login.py
  29. +13 −24 docs/tutorials/wiki2/src/authorization/tutorial/models.py
  30. +1 −0  docs/tutorials/wiki2/src/authorization/tutorial/scripts/__init__.py
  31. +35 −0 docs/tutorials/wiki2/src/authorization/tutorial/scripts/populate.py
  32. +0 −1  docs/tutorials/wiki2/src/authorization/tutorial/security.py
  33. +16 −7 docs/tutorials/wiki2/src/authorization/tutorial/tests.py
  34. +64 −12 docs/tutorials/wiki2/src/authorization/tutorial/views.py
  35. +0 −3  docs/tutorials/wiki2/src/basiclayout/README.txt
  36. +7 −1 docs/tutorials/wiki2/src/basiclayout/development.ini
  37. +1 −0  docs/tutorials/wiki2/src/basiclayout/production.ini
  38. +2 −0  docs/tutorials/wiki2/src/basiclayout/setup.py
  39. +4 −6 docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
  40. +11 −26 docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
  41. +1 −0  docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/__init__.py
  42. +35 −0 docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/populate.py
  43. +1 −1  docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
  44. +17 −8 docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py
  45. +9 −5 docs/tutorials/wiki2/src/basiclayout/tutorial/views.py
  46. +0 −3  docs/tutorials/wiki2/src/models/README.txt
  47. +7 −1 docs/tutorials/wiki2/src/models/development.ini
  48. +1 −0  docs/tutorials/wiki2/src/models/production.ini
  49. +2 −0  docs/tutorials/wiki2/src/models/setup.py
  50. +6 −6 docs/tutorials/wiki2/src/models/tutorial/__init__.py
  51. +10 −23 docs/tutorials/wiki2/src/models/tutorial/models.py
  52. +1 −0  docs/tutorials/wiki2/src/models/tutorial/scripts/__init__.py
  53. +35 −0 docs/tutorials/wiki2/src/models/tutorial/scripts/populate.py
  54. +1 −1  docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
  55. +17 −7 docs/tutorials/wiki2/src/models/tutorial/tests.py
  56. +9 −5 docs/tutorials/wiki2/src/models/tutorial/views.py
  57. +0 −3  docs/tutorials/wiki2/src/tests/README.txt
  58. +7 −1 docs/tutorials/wiki2/src/tests/development.ini
  59. +1 −0  docs/tutorials/wiki2/src/tests/production.ini
  60. +2 −1  docs/tutorials/wiki2/src/tests/setup.py
  61. +6 −19 docs/tutorials/wiki2/src/tests/tutorial/__init__.py
  62. +0 −37 docs/tutorials/wiki2/src/tests/tutorial/login.py
  63. +13 −24 docs/tutorials/wiki2/src/tests/tutorial/models.py
  64. +1 −0  docs/tutorials/wiki2/src/tests/tutorial/scripts/__init__.py
  65. +36 −0 docs/tutorials/wiki2/src/tests/tutorial/scripts/populate.py
  66. +0 −1  docs/tutorials/wiki2/src/tests/tutorial/security.py
  67. +33 −30 docs/tutorials/wiki2/src/tests/tutorial/tests.py
  68. +64 −12 docs/tutorials/wiki2/src/tests/tutorial/views.py
  69. +0 −3  docs/tutorials/wiki2/src/views/README.txt
  70. +7 −1 docs/tutorials/wiki2/src/views/development.ini
  71. +1 −0  docs/tutorials/wiki2/src/views/production.ini
  72. +2 −0  docs/tutorials/wiki2/src/views/setup.py
  73. +5 −12 docs/tutorials/wiki2/src/views/tutorial/__init__.py
  74. +9 −21 docs/tutorials/wiki2/src/views/tutorial/models.py
  75. +1 −0  docs/tutorials/wiki2/src/views/tutorial/scripts/__init__.py
  76. +35 −0 docs/tutorials/wiki2/src/views/tutorial/scripts/populate.py
  77. +1 −1  docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
  78. +12 −5 docs/tutorials/wiki2/src/views/tutorial/tests.py
  79. +13 −4 docs/tutorials/wiki2/src/views/tutorial/views.py
  80. +2 −2 docs/tutorials/wiki2/tests.rst
  81. +4 −2 pyramid/asset.py
  82. +19 −13 pyramid/authentication.py
  83. +8 −5 pyramid/authorization.py
  84. +31 −12 pyramid/config/__init__.py
  85. +6 −4 pyramid/config/factories.py
  86. +5 −3 pyramid/config/i18n.py
  87. +11 −6 pyramid/config/rendering.py
  88. +10 −6 pyramid/config/routes.py
  89. +7 −5 pyramid/config/security.py
  90. +6 −4 pyramid/config/testing.py
  91. +12 −4 pyramid/config/tweens.py
  92. +11 −5 pyramid/config/util.py
  93. +52 −35 pyramid/config/views.py
  94. +7 −5 pyramid/encode.py
  95. +7 −5 pyramid/events.py
  96. +4 −2 pyramid/exceptions.py
  97. +7 −4 pyramid/httpexceptions.py
  98. +12 −7 pyramid/i18n.py
  99. +4 −2 pyramid/interfaces.py
  100. +14 −6 pyramid/mako_templating.py
  101. +48 −6 pyramid/paster.py
  102. +22 −11 pyramid/renderers.py
  103. +16 −9 pyramid/request.py
  104. +31 −20 pyramid/router.py
  105. +8 −5 pyramid/scaffolds/__init__.py
  106. +10 −6 pyramid/scaffolds/alchemy/+package+/models.py
  107. +35 −0 pyramid/scaffolds/alchemy/+package+/scripts/populate.py
  108. +0 −27 pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl
  109. +1 −1  pyramid/scaffolds/alchemy/+package+/tests.py_tmpl
  110. +5 −3 pyramid/scaffolds/alchemy/+package+/views.py_tmpl
  111. +6 −2 pyramid/scripting.py
  112. +2 −2 pyramid/scripts/pserve.py
  113. +2 −2 pyramid/scripts/pshell.py
  114. +6 −4 pyramid/security.py
  115. +8 −5 pyramid/session.py
  116. +23 −12 pyramid/static.py
  117. +38 −21 pyramid/testing.py
  118. +71 −9 pyramid/tests/test_paster.py
  119. +19 −14 pyramid/traversal.py
  120. +5 −2 pyramid/tweens.py
  121. +15 −8 pyramid/url.py
  122. +19 −11 pyramid/urldispatch.py
  123. +12 −7 pyramid/view.py
View
3  .gitmodules
@@ -1,3 +0,0 @@
-[submodule "docs/_themes"]
- path = docs/_themes
- url = git://github.com/Pylons/pylons_sphinx_theme.git
View
17 CHANGES.txt
@@ -17,6 +17,14 @@ Features
- ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line
Pyramid" narrative docs chapter for more information.
+- Added ``get_appsettings`` API function to the ``pyramid.paster`` module.
+ This function returns the settings defined within an ``[app:...]`` section
+ in a PasteDeploy ini file.
+
+- Added ``setup_logging`` API function to the ``pyramid.paster`` module.
+ This function sets up Python logging according to the logging configuration
+ in a PasteDeploy ini file.
+
Bug Fixes
---------
@@ -60,7 +68,7 @@ Backwards Incompatibilities
``paste.httpserver`` server. Rationale: Rationale: the Paste and
PasteScript packages do not run under Python 3.
-- The ``pshell`` command (nee "paster pshell") no longer accepts a
+- The ``pshell`` command (see "paster pshell") no longer accepts a
``--disable-ipython`` command-line argument. Instead, it accepts a ``-p``
or ``--python-shell`` argument, which can be any of the values ``python``,
``ipython`` or ``bpython``.
@@ -78,6 +86,13 @@ Dependencies
- Pyramid no longer depends on the Paste or PasteScript packages.
+Documentation
+-------------
+
+- The SQLAlchemy Wiki tutorial has been updated. It now uses
+ ``@view_config`` decorators and an explicit database population script.
+
+- Minor updates to the ZODB Wiki tutorial.
Scaffolds
---------
View
1  docs/.gitignore
@@ -1,3 +1,4 @@
+_themes
_build
View
8 docs/Makefile
@@ -23,9 +23,9 @@ help:
@echo " linkcheck to check all external links for integrity"
clean:
- -rm -rf _build/* _themes
+ -rm -rf _build/*
-html: _themes
+html:
mkdir -p _build/html _build/doctrees
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
@echo
@@ -47,7 +47,7 @@ pickle:
web: pickle
-htmlhelp: _themes
+htmlhelp:
mkdir -p _build/htmlhelp _build/doctrees
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
@echo
@@ -84,5 +84,3 @@ epub:
@echo
@echo "Build finished. The epub file is in _build/epub."
-_themes:
- cd ..; git submodule update --init; cd docs
1  docs/_themes
@@ -1 +0,0 @@
-Subproject commit 03e5e5aaaeddc4c9aea887478c7e7b379a127b6f
View
11 docs/api/paster.rst
@@ -5,13 +5,10 @@
.. automodule:: pyramid.paster
- .. function:: get_app(config_uri, name=None)
+ .. autofunction:: bootstrap
- Return the WSGI application named ``name`` in the PasteDeploy
- config file specified by ``config_uri``.
+ .. autofunction:: get_app(config_uri, name=None)
- If the ``name`` is None, this will attempt to parse the name from
- the ``config_uri`` string expecting the format ``inifile#name``.
- If no name is found, the name will default to "main".
+ .. autofunction:: get_appsettings(config_uri, name=None)
- .. autofunction:: bootstrap
+ .. autofunction:: setup_logging(config_uri)
View
29 docs/conf.py
@@ -141,14 +141,29 @@ def nothing(*arg):
# -----------------------
# Add and use Pylons theme
+from subprocess import call, Popen, PIPE
+
+p = Popen('which git', shell=True, stdout=PIPE)
+git = p.stdout.read().strip()
+cwd = os.getcwd()
+_themes = os.path.join(cwd, '_themes')
+
+if not os.path.isdir(_themes):
+ call([git, 'clone', 'git://github.com/Pylons/pylons_sphinx_theme.git',
+ '_themes'])
+else:
+ os.chdir(_themes)
+ call([git, 'checkout', 'master'])
+ call([git, 'pull'])
+ os.chdir(cwd)
+
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'pyramid'
-
-html_theme_options = {
- 'github_url': 'https://github.com/Pylons/pyramid'
-}
-
+html_theme_options = dict(
+ github_url='https://github.com/Pylons/pyramid',
+ in_progress='true'
+ )
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
@@ -461,7 +476,7 @@ def resig(app, what, name, obj, options, signature, return_annotation):
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
-epub_title = 'The Pyramid Web Application Development Framework, Version 1.2'
+epub_title = 'The Pyramid Web Application Development Framework, Version 1.3dev'
epub_author = 'Chris McDonough'
epub_publisher = 'Agendaless Consulting'
epub_copyright = '2008-2011'
@@ -478,7 +493,7 @@ def resig(app, what, name, obj, options, signature, return_annotation):
epub_identifier = '0615445675'
# A unique identification for the text.
-epub_uid = 'The Pyramid Web Application Development Framework, Version 1.2'
+epub_uid = 'The Pyramid Web Application Development Framework, Version 1.3dev'
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
View
5 docs/glossary.rst
@@ -936,3 +936,8 @@ Glossary
email. See its `documentation
<https://docs.pylonsproject.org/projects/pyramid_exclog/dev/>`_.
+ console script
+ A script written to the ``bin`` (on UNIX, or ``Scripts`` on Windows)
+ directory of a Python installation or virtualenv as the result of
+ running ``setup.py install`` or ``setup.py develop``.
+
View
15 docs/narr/MyProject/myproject/static/pylons.css
@@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;}
h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;}
h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
html,body{width:100%;height:100%;}
-body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
+body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;}
a{color:#1b61d6;text-decoration:none;}
a:hover{color:#e88f00;text-decoration:underline;}
body h1,
@@ -31,19 +31,20 @@ body h2,
body h3,
body h4,
body h5,
-body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
+body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;}
#wrap{min-height:100%;}
#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;}
#header{background:#000000;top:0;font-size:14px;}
#footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;}
.header,.footer{width:750px;margin-right:auto;margin-left:auto;}
.wrapper{width:100%}
-#top,#bottom{width:100%;}
-#top{color:#000000;height:230px;
-background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top,#top-small,#bottom{width:100%;}
+#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
+#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;}
#bottom{color:#222;background-color:#ffffff;}
-.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
+.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;}
.top{padding-top:40px;}
+.top-small{padding-top:10px;}
#middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;}
.app-welcome{margin-top:25px;}
.app-name{color:#000000;font-weight:bold;}
@@ -58,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;}
form{border-style:none;}
fieldset{border-style:none;}
input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
-input[type=text]{width:205px;}
+input[type=text],input[type=password]{width:205px;}
input[type=submit]{background-color:#ddd;font-weight:bold;}
/*Opera Fix*/
body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;}
View
76 docs/narr/MyProject/myproject/templates/mytemplate.pt
@@ -1,42 +1,29 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"
- xml:lang="en"
- xmlns:tal="http://xml.zope.org/namespaces/tal">
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal">
<head>
<title>The Pyramid Web Application Development Framework</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<meta name="keywords" content="python web application" />
<meta name="description" content="pyramid web application" />
- <link rel="shortcut icon"
- href="${request.static_url('myproject:static/favicon.ico')}" />
- <link rel="stylesheet"
- href="${request.static_url('myproject:static/pylons.css')}"
- type="text/css" media="screen" charset="utf-8" />
- <link rel="stylesheet"
- href="http://fonts.googleapis.com/css?family=Neuton|Nobile:regular,i,b,bi&amp;subset=latin"
- type="text/css" media="screen" charset="utf-8" />
+ <link rel="shortcut icon" href="/static/favicon.ico" />
+ <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
<!--[if lte IE 6]>
- <link rel="stylesheet"
- href="${request.static_url('myproject:static/ie6.css')}"
- type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
</head>
<body>
<div id="wrap">
<div id="top">
<div class="top align-center">
- <div>
- <img src="${request.static_url('myproject:static/pyramid.png')}"
- width="750" height="169" alt="pyramid"/>
- </div>
+ <div><img src="/static/pyramid.png" width="750" height="169" alt="pyramid"/></div>
</div>
</div>
<div id="middle">
<div class="middle align-center">
<p class="app-welcome">
- Welcome to <span class="app-name">${project}</span>,
- an application generated by<br/>
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
the Pyramid web application development framework.
</p>
</div>
@@ -45,62 +32,45 @@
<div class="bottom">
<div id="left" class="align-right">
<h2>Search documentation</h2>
- <form method="get"
- action="http://docs.pylonsproject.org/pyramid/current/search.html">
- <input type="text" id="q" name="q" value="" />
- <input type="submit" id="x" value="Go" />
- </form>
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/current/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
</div>
<div id="right" class="align-left">
<h2>Pyramid links</h2>
<ul class="links">
<li>
- <a href="http://pylonsproject.org">
- Pylons Website
- </a>
+ <a href="http://pylonsproject.org">Pylons Website</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">
- Narrative Documentation
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#narrative-documentation">Narrative Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">
- API Documentation
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#api-documentation">API Documentation</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">
- Tutorials
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#tutorials">Tutorials</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">
- Change History
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#change-history">Change History</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">
- Sample Applications
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#sample-applications">Sample Applications</a>
</li>
<li>
- <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">
- Support and Development
- </a>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/current/#support-and-development">Support and Development</a>
</li>
<li>
- <a href="irc://irc.freenode.net#pyramid">
- IRC Channel
- </a>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
</li>
- </ul>
+ </ul>
</div>
</div>
</div>
</div>
<div id="footer">
- <div class="footer">&copy; Copyright 2008-2010, Agendaless Consulting.</div>
+ <div class="footer">&copy; Copyright 2008-2011, Agendaless Consulting.</div>
</div>
</body>
</html>
View
37 docs/tutorials/wiki/authorization.rst
@@ -128,18 +128,24 @@ We'll also add a ``logout`` view to our application and provide a link
to it. This view will clear the credentials of the logged in user and
redirect back to the front page.
-We'll add a different file (for presentation convenience) to add login
-and logout views. Add a file named ``login.py`` to your application
-(in the same directory as ``views.py``) with the following content:
+We'll add these views to the existing ``views.py`` file we have in our
+project. Here's what the ``login`` view callable will look like:
-.. literalinclude:: src/authorization/tutorial/login.py
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: login
:linenos:
:language: python
-Note that the ``login`` view callable in the ``login.py`` file has *two* view
-configuration decorators. The order of these decorators is unimportant.
-Each just adds a different :term:`view configuration` for the ``login`` view
-callable.
+Here's what the ``logout`` view callable will look like:
+
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: logout
+ :linenos:
+ :language: python
+
+Note that the ``login`` view callable has *two* view configuration
+decorators. The order of these decorators is unimportant. Each just adds a
+different :term:`view configuration` for the ``login`` view callable.
The first view configuration decorator configures the ``login`` view callable
so it will be invoked when someone visits ``/login`` (when the context is a
@@ -156,14 +162,18 @@ login form. Before being allowed to continue on to the add or edit form, he
will have to provide credentials that give him permission to add or edit via
this login form.
+Note that we're relying on some additional imports within the bodies of these
+views (e.g. ``remember`` and ``forget``). We'll see a rendering of the
+entire views.py file a little later here to show you where those come from.
+
Change Existing Views
~~~~~~~~~~~~~~~~~~~~~
-Then we need to change each of our ``view_page``, ``edit_page`` and
-``add_page`` views in ``views.py`` to pass a "logged in" parameter
-into its template. We'll add something like this to each view body:
+In order to indicate whether the current user is logged in, we need to change
+each of our ``view_page``, ``edit_page`` and ``add_page`` views in
+``views.py`` to pass a "logged in" parameter into its template. We'll add
+something like this to each view body:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -174,7 +184,6 @@ We'll then change the return value of each view that has an associated
``renderer`` to pass the resulting ``logged_in`` value to the
template. For example:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -219,7 +228,7 @@ Add the ``login.pt`` Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Add a ``login.pt`` template to your templates directory. It's
-referred to within the login view we just added to ``login.py``.
+referred to within the login view we just added to ``views.py``.
.. literalinclude:: src/authorization/tutorial/templates/login.pt
:language: xml
View
4 docs/tutorials/wiki/basiclayout.rst
@@ -10,8 +10,8 @@ The source code for this tutorial stage can be browsed via
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/>`_.
-App Startup with ``__init__.py``
---------------------------------
+Appplication Configuration with ``__init__.py``
+------------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
View
43 docs/tutorials/wiki/src/authorization/tutorial/login.py
@@ -1,43 +0,0 @@
-from pyramid.httpexceptions import HTTPFound
-
-from pyramid.security import remember
-from pyramid.security import forget
-from pyramid.view import view_config
-
-from .security import USERS
-
-@view_config(context='.models.Wiki', name='login',
- renderer='templates/login.pt')
-@view_config(context='pyramid.httpexceptions.HTTPForbidden',
- renderer='templates/login.pt')
-def login(request):
- login_url = request.resource_url(request.context, 'login')
- referrer = request.url
- if referrer == login_url:
- referrer = '/' # never use the 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(
- message = message,
- url = request.application_url + '/login',
- came_from = came_from,
- login = login,
- password = password,
- )
-
-@view_config(context='.models.Wiki', name='logout')
-def logout(request):
- headers = forget(request)
- return HTTPFound(location = request.resource_url(request.context),
- headers = headers)
View
45 docs/tutorials/wiki/src/authorization/tutorial/views.py
@@ -2,9 +2,16 @@
import re
from pyramid.httpexceptions import HTTPFound
+
from pyramid.view import view_config
-from pyramid.security import authenticated_userid
+from pyramid.security import (
+ authenticated_userid,
+ remember,
+ forget,
+ )
+
+from .security import USERS
from .models import Page
# regular expression used to find WikiWords
@@ -72,3 +79,39 @@ def edit_page(context, request):
return dict(page = context,
save_url = request.resource_url(context, 'edit_page'),
logged_in = logged_in)
+
+@view_config(context='.models.Wiki', name='login',
+ renderer='templates/login.pt')
+@view_config(context='pyramid.httpexceptions.HTTPForbidden',
+ renderer='templates/login.pt')
+def login(request):
+ login_url = request.resource_url(request.context, 'login')
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use the 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(
+ message = message,
+ url = request.application_url + '/login',
+ came_from = came_from,
+ login = login,
+ password = password,
+ )
+
+@view_config(context='.models.Wiki', name='logout')
+def logout(request):
+ headers = forget(request)
+ return HTTPFound(location = request.resource_url(request.context),
+ headers = headers)
View
43 docs/tutorials/wiki/src/tests/tutorial/login.py
@@ -1,43 +0,0 @@
-from pyramid.httpexceptions import HTTPFound
-
-from pyramid.security import remember
-from pyramid.security import forget
-from pyramid.view import view_config
-
-from .security import USERS
-
-@view_config(context='.models.Wiki', name='login',
- renderer='templates/login.pt')
-@view_config(context='pyramid.httpexceptions.HTTPForbidden',
- renderer='templates/login.pt')
-def login(request):
- login_url = request.resource_url(request.context, 'login')
- referrer = request.url
- if referrer == login_url:
- referrer = '/' # never use the 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(
- message = message,
- url = request.application_url + '/login',
- came_from = came_from,
- login = login,
- password = password,
- )
-
-@view_config(context='.models.Wiki', name='logout')
-def logout(request):
- headers = forget(request)
- return HTTPFound(location = request.resource_url(request.context),
- headers = headers)
View
45 docs/tutorials/wiki/src/tests/tutorial/views.py
@@ -2,9 +2,16 @@
import re
from pyramid.httpexceptions import HTTPFound
+
from pyramid.view import view_config
-from pyramid.security import authenticated_userid
+from pyramid.security import (
+ authenticated_userid,
+ remember,
+ forget,
+ )
+
+from .security import USERS
from .models import Page
# regular expression used to find WikiWords
@@ -72,3 +79,39 @@ def edit_page(context, request):
return dict(page = context,
save_url = request.resource_url(context, 'edit_page'),
logged_in = logged_in)
+
+@view_config(context='.models.Wiki', name='login',
+ renderer='templates/login.pt')
+@view_config(context='pyramid.httpexceptions.HTTPForbidden',
+ renderer='templates/login.pt')
+def login(request):
+ login_url = request.resource_url(request.context, 'login')
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use the 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(
+ message = message,
+ url = request.application_url + '/login',
+ came_from = came_from,
+ login = login,
+ password = password,
+ )
+
+@view_config(context='.models.Wiki', name='logout')
+def logout(request):
+ headers = forget(request)
+ return HTTPFound(location = request.resource_url(request.context),
+ headers = headers)
View
7 docs/tutorials/wiki/tests.rst
@@ -9,10 +9,9 @@ that it continues to work after some changes are made in the future.
Test the Models
===============
-We write tests for the model
-classes and the appmaker. Changing ``tests.py``, we'll write a separate test
-class for each model class, and we'll write a test class for the
-``appmaker``.
+We write tests for the model classes and the appmaker. Changing
+``tests.py``, we'll write a separate test class for each model class, and
+we'll write a test class for the ``appmaker``.
To do so, we'll retain the ``tutorial.tests.ViewTests`` class provided as a
result of the ``zodb`` project generator. We'll add three test
View
153 docs/tutorials/wiki2/authorization.rst
@@ -4,27 +4,22 @@
Adding Authorization
====================
-Our application currently allows anyone with access to the server to
-view, edit, and add pages to our wiki. For purposes of demonstration
-we'll change our application to allow only people whom possess a
-specific username (`editor`) to add and edit wiki pages but we'll
-continue allowing anyone with access to the server to view pages.
-:app:`Pyramid` provides facilities for :term:`authorization` and
-:term:`authentication`. We'll make use of both features to provide security
-to our application.
-
-We will add an :term:`authentication policy` and an
-:term:`authorization policy` to our :term:`application
-registry`, add a ``security.py`` module, create a :term:`root factory`
-with an :term:`ACL`, and add :term:`permission` declarations to
-the ``edit_page`` and ``add_page`` views.
-
-Then we will add ``login`` and ``logout`` views, and modify the
-existing views to make them return a ``logged_in`` flag to the
-renderer.
-
-Finally, we will add a ``login.pt`` template and change the existing
-``view.pt`` and ``edit.pt`` to show a "Logout" link when not logged in.
+:app:`Pyramid` provides facilities for :term:`authentication` and
+:term:`authorization`. We'll make use of both features to provide security
+to our application. Our application currently allows anyone with access to
+the server to view, edit, and add pages to our wiki. We'll change our
+application to allow only people whom possess a specific username (`editor`)
+to add and edit wiki pages but we'll continue allowing anyone with access to
+the server to view pages.
+
+To do so, we'll add an :term:`authentication policy` and an
+:term:`authorization policy`. We'll also add a ``security.py`` module,
+create a :term:`root factory` with an :term:`ACL`, and add :term:`permission`
+declarations to the ``edit_page`` and ``add_page`` views. Then we'll add
+``login`` and ``logout`` views, and modify the existing views to make them
+return a ``logged_in`` flag to the renderer. Finally, we will add a
+``login.pt`` template and change the existing ``view.pt`` and ``edit.pt`` to
+show a "Logout" link when not logged in.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/authorization/
@@ -54,7 +49,7 @@ inside our ``models.py`` file. Add the following statements to your
``models.py`` file:
.. literalinclude:: src/authorization/tutorial/models.py
- :lines: 3-4,45-50
+ :lines: 1-4,35-39
:linenos:
:language: python
@@ -92,14 +87,14 @@ We'll change our ``__init__.py`` file to enable an
declarative security checking. We need to import the new policies:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 2-3,8
+ :lines: 2-3,7
:linenos:
:language: python
Then, we'll add those policies to the configuration:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 15-21
+ :lines: 16-22
:linenos:
:language: python
@@ -111,49 +106,12 @@ represented by this policy: it is required. The ``callback`` is a
``groupfinder`` function in the current directory's ``security.py`` file. We
haven't added that module yet, but we're about to.
-We'll also change ``__init__.py``, adding a call to
-:meth:`pyramid.config.Configurator.add_view` that points at our ``login``
-:term:`view callable`. This is also known as a :term:`forbidden view`:
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 25,41-43
- :linenos:
- :language: python
-
-A forbidden view configures our newly created login view to show up when
-:app:`Pyramid` detects that a view invocation can not be authorized.
-
-A ``logout`` :term:`view callable` will allow users to log out later:
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 26,34
- :linenos:
- :language: python
-
-We'll also add ``permission`` arguments with the value ``edit`` to the
-``edit_page`` and ``add_page`` views. This indicates that the view
-callables which these views reference cannot be invoked without the
-authenticated user possessing the ``edit`` permission with respect to the
-current context.
-
-.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 37-40
- :linenos:
- :language: python
-
-Adding these ``permission`` arguments causes Pyramid to make the
-assertion that only users who possess the effective ``edit`` permission at
-the time of the request may invoke those two views. We've granted the
-``group:editors`` principal the ``edit`` permission at the root model via its
-ACL, so only the a user whom is a member of the group named ``group:editors``
-will able to invoke the views associated with the ``add_page`` or
-``edit_page`` routes.
-
Viewing Your Changes
~~~~~~~~~~~~~~~~~~~~
-When we're done configuring a root factory, adding an authorization policy,
-and adding views, your application's ``__init__.py`` will look like this:
+When we're done configuring a root factory, adding a authentication and
+authorization policies, and adding routes for ``/login`` and ``/logout``,
+your application's ``__init__.py`` will look like this:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
@@ -191,30 +149,54 @@ views, the ``editor`` user should be able to add and edit pages.
Adding Login and Logout Views
-----------------------------
-We'll add a ``login`` view callable which renders a login form and
-processes the post from the login form, checking credentials.
+To our ``views.py`` we'll add a ``login`` view callable which renders a login
+form and processes the post from the login form, checking credentials.
We'll also add a ``logout`` view callable to our application and
provide a link to it. This view will clear the credentials of the
logged in user and redirect back to the front page.
-We'll add a different file (for presentation convenience) to add login
-and the logout view callables. Add a file named ``login.py`` to your
-application (in the same directory as ``views.py``) with the following
-content:
+The ``login`` view callable will look something like this:
-.. literalinclude:: src/authorization/tutorial/login.py
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: login
:linenos:
:language: python
+The ``logout`` view callable will look something like this:
+
+.. literalinclude:: src/authorization/tutorial/views.py
+ :pyobject: logout
+ :linenos:
+ :language: python
+
+The ``login`` view callable is decorated with two ``@view_config``
+decorators, one which associates it with the ``login`` route, the other which
+associates it with the ``HTTPForbidden`` context. The one which associates
+it with the ``login`` route makes it visible when we visit ``/login``. The
+one which associates it with the ``HTTPForbidden`` context makes it the
+:term:`forbidden view`. The forbidden view is displayed whenever Pyramid or
+your application raises an HTTPForbidden exception. In this case, we'll be
+relying on the forbidden view to show the login form whenver someone attempts
+to execute an action which they're not yet authorized to perform.
+
+The ``logout`` view callable is decorated with a ``@view_config`` decorator
+which associates it with the ``logout`` route. This makes it visible when we
+visit ``/login``.
+
+We'll need to import some stuff to service the needs of these two functions:
+the ``HTTPForbidden`` exception, a number of values from the
+``pyramid.security`` module, and a value from our newly added
+``tutorial.security`` package.
+
Changing Existing Views
-----------------------
Then we need to change each of our ``view_page``, ``edit_page`` and
-``add_page`` views in ``views.py`` to pass a "logged in" parameter to its
-template. We'll add something like this to each view body:
+``add_page`` view callables in ``views.py``. Within each of these views,
+we'll need to pass a "logged in" parameter to its template. We'll add
+something like this to each view body:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -224,7 +206,6 @@ template. We'll add something like this to each view body:
We'll then change the return value of these views to pass the `resulting
`logged_in`` value to the template, e.g.:
-.. ignore-next-block
.. code-block:: python
:linenos:
@@ -233,6 +214,28 @@ We'll then change the return value of these views to pass the `resulting
logged_in = logged_in,
edit_url = edit_url)
+We'll also need to add a ``permission`` value to the ``@view_config``
+decorator for each of the ``add_page`` and ``edit_page`` view callables. For
+each, we'll add ``permission='edit'``, for example:
+
+.. code-block:: python
+ :linenos:
+
+ @view_config(route_name='edit_page', renderer='templates/edit.pt',
+ permission='edit')
+
+See the ``permission='edit'`` we added there? This indicates that the view
+callables which these views reference cannot be invoked without the
+authenticated user possessing the ``edit`` permission with respect to the
+current :term:`context`.
+
+Adding these ``permission`` arguments causes Pyramid to make the assertion
+that only users who possess the effective ``edit`` permission at the time of
+the request may invoke those two views. We've granted the ``group:editors``
+principal the ``edit`` permission at the root model via its ACL, so only the
+a user whom is a member of the group named ``group:editors`` will able to
+invoke the views associated with the ``add_page`` or ``edit_page`` routes.
+
Adding the ``login.pt`` Template
--------------------------------
View
195 docs/tutorials/wiki2/basiclayout.rst
@@ -2,82 +2,96 @@
Basic Layout
============
-The starter files generated by the ``alchemy`` scaffold are
-basic, but they provide a good orientation for the high-level patterns common
-to most :term:`url dispatch` -based :app:`Pyramid` projects.
+The starter files generated by the ``alchemy`` scaffold are very basic, but
+they provide a good orientation for the high-level patterns common to most
+:term:`url dispatch` -based :app:`Pyramid` projects.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/
<http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/>`_.
-App Startup with ``__init__.py``
---------------------------------
+Application Configuration with ``__init__.py``
+----------------------------------------------
A directory on disk can be turned into a Python :term:`package` by containing
an ``__init__.py`` file. Even if empty, this marks a directory as a Python
-package. We use ``__init__.py`` both as a package marker and to contain
-configuration code.
+package. We use ``__init__.py`` both as a marker indicating the directory
+it's contained within is a package, and to contain configuration code. Our
+``__init__.py`` file will look like this:
-The generated ``development.ini`` file is read by ``pserve`` which looks for
-the application module in the ``use`` variable of the ``app:main``
-section. The *entry point* is defined in the Setuptools configuration of this
-module, specifically in the ``setup.py`` file. For this tutorial, the *entry
-point* is defined as ``tutorial:main`` and points to a function named
-``main``.
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :linenos:
+ :language: py
-First we need some imports to support later code:
+Let's go over this piece-by-piece. First, we need some imports to support
+later code:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:end-before: main
:linenos:
:language: py
-Next we define the main function and create a SQLAlchemy database engine from
-the ``sqlalchemy.`` prefixed settings in the ``development.ini`` file's
-``[app:main]`` section. This will be a URI (something like
-``sqlite://``):
+``__init__.py`` defines a function named ``main``. Here is the entirety of
+the ``main`` function we've defined in our ``__init__.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :pyobject: main
+ :linenos:
+ :language: py
+
+When you invoke the ``pserve development.ini`` command, the ``main`` function
+above is executed. It accepts some settings and returns a :term:`WSGI`
+application. You can read :ref:`startup_chapter` for details about *how*
+this function is found and called when you run ``pserve``, but for purposes
+of brevity, we'll elide the details here.
+
+The main function first creates a SQLAlchemy database engine using
+``engine_from_config`` from the ``sqlalchemy.`` prefixed settings in the
+``development.ini`` file's ``[app:main]`` section. This will be a URI
+(something like ``sqlite://``):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 6-9
+ :lines: 9
:linenos:
:language: py
-We then initialize our SQL database using SQLAlchemy, passing
-it the engine:
+``main`` then initializes our SQL database using SQLAlchemy, passing it the
+engine:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 10
:language: py
-The next step is to construct a :term:`Configurator`:
+The next step of ``main`` is to construct a :term:`Configurator` object:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 11
:language: py
``settings`` is passed to the Configurator as a keyword argument with the
-dictionary values passed by PasteDeploy as the ``**settings`` argument. This
-will be a dictionary of settings parsed from the ``.ini`` file, which
-contains deployment-related values such as ``pyramid.reload_templates``,
+dictionary values passed as the ``**settings`` argument. This will be a
+dictionary of settings parsed from the ``.ini`` file, which contains
+deployment-related values such as ``pyramid.reload_templates``,
``db_string``, etc.
-We now can call :meth:`pyramid.config.Configurator.add_static_view` with the
-arguments ``static`` (the name), and ``tutorial:static`` (the path):
+``'main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with
+two arguments: ``static`` (the name), and ``static`` (the path):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
:lines: 12
:language: py
-This registers a static resource view which will match any URL that starts with
-``/static/``. This will serve up static resources for us from within the
-``static`` directory of our ``tutorial`` package, in this case,
-via ``http://localhost:6543/static/`` and below. With this declaration,
-we're saying that any URL that starts with ``/static`` should go to the
-static view; any remainder of its path (e.g. the ``/foo`` in
-``/static/foo``) will be used to compose a path to a static file resource,
-such as a CSS file.
+This registers a static resource view which will match any URL that starts
+with the prefix ``/static`` (by virtue of the first argument to add_static
+view). This will serve up static resources for us from within the ``static``
+directory of our ``tutorial`` package, in this case, via
+``http://localhost:6543/static/`` and below (by virtue of the second argument
+to add_static_view). With this declaration, we're saying that any URL that
+starts with ``/static`` should go to the static view; any remainder of its
+path (e.g. the ``/foo`` in ``/static/foo``) will be used to compose a path to
+a static file resource, such as a CSS file.
-Using the configurator we can also register a :term:`route configuration`
+Using the configurator ``main`` also registers a :term:`route configuration`
via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
@@ -88,44 +102,77 @@ used when the URL is ``/``:
Since this route has a ``pattern`` equalling ``/`` it is the route that will
be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``.
-Mapping the ``home`` route to code is done by registering a view. You will
-use :meth:`pyramid.config.Configurator.add_view` in :term:`URL dispatch` to
-register views for the routes, mapping your patterns to code:
+``main`` next calls the ``scan`` method of the configurator, which will
+recursively scan our ``tutorial`` package, looking for ``@view_config`` (and
+other special) decorators. When it finds a ``@view_config`` decorator, a
+view configuration will be registered, which will allow one of our
+application URLs to be mapped to some code.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 14-15
+ :lines: 14
:language: py
-The first positional ``add_view`` argument ``tutorial.views.my_view`` is the
-dotted name to a *function* we write (generated by the
-``alchemy`` scaffold) that is given a ``request`` object and
-which returns a response or a dictionary. This view also names a
-``renderer``, which is a template which lives in the ``templates``
-subdirectory of the package. When the ``tutorial.views.my_view`` view
-returns a dictionary, a :term:`renderer` will use this template to create a
-response.
-
-Finally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app`
-method to return a :term:`WSGI` application:
+Finally, ``main`` is finished configuring things, so it uses the
+:meth:`pyramid.config.Configurator.make_wsgi_app` method to return a
+:term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 16
+ :lines: 15
:language: py
-Our final ``__init__.py`` file will look like this:
+View Declarations via ``views.py``
+----------------------------------
- .. literalinclude:: src/basiclayout/tutorial/__init__.py
+Mapping a :term:`route` to code that will be executed when that route's
+pattern matches is done by registering a :term:`view configuration`. Our
+application uses the :meth:`pyramid.view.view_config` decorator to map view
+callables to each route, thereby mapping URL patterns to code.
+
+Here is the entirety of code in the ``views.py`` file within our package:
+
+ .. literalinclude:: src/basiclayout/tutorial/views.py
:linenos:
:language: py
+The important part to point out here is the ``@view_config`` decorator which
+sits atop the ``my_view`` function. In fact, ``@view_config`` is so
+important that we're going to ignore the rest of the code in the module at
+this point just to explain it. The ``@view_config`` decorator associates the
+function it decorates with a :term:`view configuration`. The view
+configuration names a ``route_name`` (``home``), and names a ``renderer``,
+which is a template which lives in the ``templates`` subdirectory of the
+package.
+
+As the result of this view configuration, when the pattern associated with
+the view named ``home`` is matched during a request, the function named
+``my_view`` will be executed. The the function named ``my_view`` returns a
+dictionary; the renderer will use the ``templates/mytemplate.pt`` template to
+create a response based on the values in the dictionary.
+
+Note that the decorated function named ``my_view`` accepts a single argument
+named ``request``. This is the standard call signature for a Pyramid
+:term:`view callable`.
+
+Remember in our ``__init__.py`` when we executed the
+:meth:`pyramid.config.Configurator.scan` method, e.g. ``config.scan()``? The
+purpose of calling the scan method was to find and process this
+``@view_config`` decorator in order to create a view configuration within our
+application. Without being processed by ``scan``, the decorator effectively
+does nothing. ``@view_config`` is inert without being detected via a
+:term:`scan`.
+
Content Models with ``models.py``
---------------------------------
-In a SQLAlchemy-based application, a *model* object is an object
-composed by querying the SQL database which backs an application.
-SQLAlchemy is an "object relational mapper" (an ORM). The
-``models.py`` file is where the ``alchemy`` scaffold
-put the classes that implement our models.
+In a SQLAlchemy-based application, a *model* object is an object composed by
+querying the SQL database. The ``models.py`` file is where the ``alchemy``
+scaffold put the classes that implement our models.
+
+Here is the complete source for ``models.py``:
+
+ .. literalinclude:: src/basiclayout/tutorial/models.py
+ :linenos:
+ :language: py
Let's take a look. First, we need some imports to support later code.
@@ -137,7 +184,7 @@ Let's take a look. First, we need some imports to support later code.
Next we set up a SQLAlchemy "DBSession" object:
.. literalinclude:: src/basiclayout/tutorial/models.py
- :lines: 15-16
+ :lines: 16
:linenos:
:language: py
@@ -161,30 +208,6 @@ within the ``__init__`` function itself. The ``MyModel`` class also has a
``__tablename__`` attribute. This informs SQLAlchemy which table to use to
store the data representing instances of this class.
-Next we define a function named ``populate`` which adds a single
-model instance into our SQL storage and commits a transaction:
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :pyobject: populate
- :linenos:
- :language: py
-
-The function doesn't do a lot in this case, but it's there to illustrate
-how an application requiring many objects to be set up could work.
-
-Lastly we have a function named ``initialize_sql`` which receives a SQL
-database engine and binds it to our SQLAlchemy DBSession object. It also
-calls the ``populate`` function, to do initial database population. This
-is the initialization function that is called from __init__.py above.
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :pyobject: initialize_sql
- :linenos:
- :language: py
-
-Here is the complete source for ``models.py``:
-
- .. literalinclude:: src/basiclayout/tutorial/models.py
- :linenos:
- :language: py
+That's about all there is to it to models, views, and initialization code in
+our stock application.
View
103 docs/tutorials/wiki2/definingmodels.rst
@@ -15,29 +15,25 @@ Making Edits to ``models.py``
.. note::
- There is nothing automagically special about the filename
- ``models.py``. A project may have many models throughout its
- codebase in arbitrarily-named files. Files implementing models
- often have ``model`` in their filenames (or they may live in a
- Python subpackage of your application package named ``models``) ,
- but this is only by convention.
+ There is nothing automagically special about the filename ``models.py``. A
+ project may have many models throughout its codebase in arbitrarily-named
+ files. Files implementing models often have ``model`` in their filenames
+ (or they may live in a Python subpackage of your application package named
+ ``models``) , but this is only by convention.
-The first thing we want to do is remove the stock ``MyModel`` class from the
-generated ``models.py`` file. The ``MyModel`` class is only a sample and
-we're not going to use it.
-
-Next, we'll remove the :class:`sqlalchemy.Unicode` import and replace it
-with :class:`sqlalchemy.Text`.
+Here's what our ``models.py`` file should look like after this step:
.. literalinclude:: src/models/tutorial/models.py
- :lines: 5
:linenos:
:language: py
-Then, we'll add a ``Page`` class. Because this is a SQLAlchemy
-application, this class should inherit from an instance of
-:class:`sqlalchemy.ext.declarative.declarative_base`. Declarative
-SQLAlchemy models are easier to use than directly-mapped ones.
+The first thing we've done is to do is remove the stock ``MyModel`` class
+from the generated ``models.py`` file. The ``MyModel`` class is only a
+sample and we're not going to use it.
+
+Then, we added a ``Page`` class. Because this is a SQLAlchemy application,
+this class inherits from an instance of
+:class:`sqlalchemy.ext.declarative.declarative_base`.
.. literalinclude:: src/models/tutorial/models.py
:pyobject: Page
@@ -54,24 +50,18 @@ in the table. The ``name`` attribute will be a text attribute, each value of
which needs to be unique within the column. The ``data`` attribute is a text
attribute that will hold the body of each page.
-We'll also remove our ``populate`` function. We'll inline the populate step
-into ``initialize_sql``, changing our ``initialize_sql`` function to add a
-FrontPage object to our database at startup time.
+Changing ``scripts/populate.py``
+--------------------------------
-.. literalinclude:: src/models/tutorial/models.py
- :pyobject: initialize_sql
- :linenos:
- :language: python
+We haven't looked at the guts of this file yet, but within the ``scripts``
+directory of your ``tutorial`` package is a file named ``populate.py``. Code
+in this file is executed whenever we run the ``populate_tutorial`` command
+(as we did in the installation step of this tutorial).
-Here, we're using a slightly different binding syntax. It is otherwise
-largely the same as the ``initialize_sql`` in the pcreate-generated
-``models.py``.
-
-Our ``DBSession`` assignment stays the same as the original generated
-``models.py``.
-
-Looking at the Result of all Our Edits to ``models.py``
--------------------------------------------------------
+Since we've changed our model, we need to make changes to our ``populate.py``
+script. In particular, we'll replace our import of ``MyModel`` with one of
+``Page`` and we'll change the very end of the script to create a ``Page``
+rather than a ``MyModel`` and add it to our ``DBSession``.
The result of all of our edits to ``models.py`` will end up looking
something like this:
@@ -80,6 +70,53 @@ something like this:
:linenos:
:language: python
+Repopulating the Database
+-------------------------
+
+Because our model has changed, in order to repopulate the database, we need
+to rerun the ``populate_tutorial`` command to pick up the changes you've made
+to both the models.py file and to the populate.py file. From the root of the
+``tutorial`` project, directory execute the following commands.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/populate_tutorial development.ini
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\populate_tutorial development.ini
+
+Success will look something like this::
+
+ 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ PRAGMA table_info("pages")
+ 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-27 01:22:45,277 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ CREATE TABLE pages (
+ id INTEGER NOT NULL,
+ name TEXT,
+ data TEXT,
+ PRIMARY KEY (id),
+ UNIQUE (name)
+ )
+
+
+ 2011-11-27 01:22:45,278 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-27 01:22:45,397 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+ 2011-11-27 01:22:45,400 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ BEGIN (implicit)
+ 2011-11-27 01:22:45,401 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ INSERT INTO pages (name, data) VALUES (?, ?)
+ 2011-11-27 01:22:45,401 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ ('FrontPage', 'This is the front page')
+ 2011-11-27 01:22:45,402 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+
Viewing the Application in a Browser
------------------------------------
View
161 docs/tutorials/wiki2/definingviews.rst
@@ -2,34 +2,19 @@
Defining Views
==============
-A :term:`view callable` in a :term:`url dispatch` -based :app:`Pyramid`
-application is typically a simple Python function that accepts a single
-parameter named :term:`request`. A view callable is assumed to return a
-:term:`response` object.
-
-.. note::
-
- A :app:`Pyramid` view can also be defined as callable
- which accepts *two* arguments: a :term:`context` and a
- :term:`request`. You'll see this two-argument pattern used in
- other :app:`Pyramid` tutorials and applications. Either calling
- convention will work in any :app:`Pyramid` application; the
- calling conventions can be used interchangeably as necessary. In
- :term:`url dispatch` based applications, however, the context
- object is rarely used in the view body itself, so within this
- tutorial we define views as callables that accept only a request to
- avoid the visual "noise". If you do need the ``context`` within a
- view function that only takes the request as a single argument, you
- can obtain it via ``request.context``.
-
-The request passed to every view that is called as the result of a route
-match has an attribute named ``matchdict`` that contains the elements placed
-into the URL by the ``pattern`` of a ``route`` statement. For instance, if a
-call to :meth:`pyramid.config.Configurator.add_route` in ``__init__.py`` had
-the pattern ``{one}/{two}``, and the URL at ``http://example.com/foo/bar``
-was invoked, matching this pattern, the ``matchdict`` dictionary attached to
-the request passed to the view would have a ``'one'`` key with the value
-``'foo'`` and a ``'two'`` key with the value ``'bar'``.
+A :term:`view callable` in a :app:`Pyramid` application is typically a simple
+Python function that accepts a single parameter named :term:`request`. A
+view callable is assumed to return a :term:`response` object.
+
+The request object passed to every view that is called as the result of a
+route match has an attribute named ``matchdict`` that contains the elements
+placed into the URL by the ``pattern`` of a ``route`` statement. For
+instance, if a call to :meth:`pyramid.config.Configurator.add_route` in
+``__init__.py`` had the pattern ``{one}/{two}``, and the URL at
+``http://example.com/foo/bar`` was invoked, matching this pattern, the
+``matchdict`` dictionary attached to the request passed to the view would
+have a ``'one'`` key with the value ``'foo'`` and a ``'two'`` key with the
+value ``'bar'``.
The source code for this tutorial stage can be browsed at
`http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/views/
@@ -52,24 +37,56 @@ Our resulting ``setup.py`` should look like so:
:linenos:
:language: python
-.. note:: After these new dependencies are added, you will need to
- rerun ``python setup.py develop`` inside the root of the
- ``tutorial`` package to obtain and register the newly added
- dependency package.
+Running ``setup.py develop``
+============================
+
+Since a new software dependency was added, you will need to rerun ``python
+setup.py develop`` inside the root of the ``tutorial`` package to obtain and
+register the newly added dependency distribution.
+
+Make sure your current working directory is the root of the project (the
+directory in which setup.py lives) and execute the following command.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ cd tutorial
+ $ ../bin/python setup.py develop
-Adding View Functions
-=====================
+On Windows:
-We'll get rid of our ``my_view`` view function in our ``views.py`` file.
-It's only an example and isn't relevant to our application.
+.. code-block:: text
-Then we're going to add four :term:`view callable` functions to our
-``views.py`` module. One view callable (named ``view_wiki``) will display
-the wiki itself (it will answer on the root URL), another named ``view_page``
-will display an individual page, another named ``add_page`` will allow a page
-to be added, and a final view callable named ``edit_page`` will allow a page
-to be edited. We'll describe each one briefly and show the resulting
-``views.py`` file afterward.
+ c:\pyramidtut> cd tutorial
+ c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
+
+Success executing this command will end with a line to the console something
+like::
+
+ Finished processing dependencies for tutorial==0.0
+
+Changing the ``views.py`` File
+==============================
+
+We're going to edit our ``views.py`` in a rather major way. The result of
+all of our edits to ``views.py`` will leave it looking like this:
+
+.. literalinclude:: src/views/tutorial/views.py
+ :linenos:
+ :language: python
+
+We've gotten rid of the ``my_view`` view function and its decorator that was
+added when we originally rendered the ``alchemy`` scaffold. It was only an
+example and isn't relevant to our application.
+
+Then we added four :term:`view callable` functions to our ``views.py``
+module. One view callable (named ``view_wiki``) will display the wiki itself
+(it will answer on the root URL), another named ``view_page`` will display an
+individual page, another named ``add_page`` will allow a page to be added,
+and a final view callable named ``edit_page`` will allow a page to be edited.
+We'll describe each one briefly and show the resulting ``views.py`` file
+afterward.
.. note::
@@ -195,16 +212,6 @@ If the view execution *is* a result of a form submission (if the expression
attribute of the page object. It then redirects to the ``view_page`` view
of the wiki page.
-Viewing the Result of all Our Edits to ``views.py``
-===================================================
-
-The result of all of our edits to ``views.py`` will leave it looking
-like this:
-
-.. literalinclude:: src/views/tutorial/views.py
- :linenos:
- :language: python
-
Adding Templates
================
@@ -270,47 +277,41 @@ subdirectories) and are just referred to by URL or by using the convenience
method ``static_url``
e.g. ``request.static_url('{{package}}:static/foo.css')`` within templates.
-Mapping Views to URLs in ``__init__.py``
-========================================
+Adding Routes to ``__init__.py``
+================================
The ``__init__.py`` file contains
-:meth:`pyramid.config.Configurator.add_view` calls which serve to map
-routes via :term:`url dispatch` to views. First, we’ll get rid of the
-existing route created by the template using the name ``'home'``. It’s only an
-example and isn’t relevant to our application.
+:meth:`pyramid.config.Configurator.add_route` calls which serve to add routes
+to our application. First, we’ll get rid of the existing route created by
+the template using the name ``'home'``. It’s only an example and isn’t
+relevant to our application.
We then need to add four calls to ``add_route``. Note that the *ordering* of
these declarations is very important. ``route`` declarations are matched in
the order they're found in the ``__init__.py`` file.
#. Add a declaration which maps the pattern ``/`` (signifying the root URL)
- to the route named ``view_wiki``.
+ to the route named ``view_wiki``. It maps to our ``view_wiki`` view
+ callable by virtue of the ``@view_config`` attached to the ``view_wiki``
+ view function indicating ``route_name='view_wiki'``.
#. Add a declaration which maps the pattern ``/{pagename}`` to the route named
- ``view_page``. This is the regular view for a page.
+ ``view_page``. This is the regular view for a page. It maps
+ to our ``view_page`` view callable by virtue of the ``@view_config``
+ attached to the ``view_page`` view function indicating
+ ``route_name='view_page'``.
#. Add a declaration which maps the pattern ``/add_page/{pagename}`` to the
- route named ``add_page``. This is the add view for a new page.
+ route named ``add_page``. This is the add view for a new page. It maps
+ to our ``add_page`` view callable by virtue of the ``@view_config``
+ attached to the ``add_page`` view function indicating
+ ``route_name='add_page'``.
#. Add a declaration which maps the pattern ``/{pagename}/edit_page`` to the
- route named ``edit_page``. This is the edit view for a page.
-
-After we've defined the routes for our application, we can register views
-to handle the processing and rendering that needs to happen when each route is
-requested.
-
-#. Add a declaration which maps the ``view_wiki`` route to the view named
- ``view_wiki`` in our ``views.py`` file. This is the :term:`default view`
- for the wiki.
-
-#. Add a declaration which maps the ``view_page`` route to the view named
- ``view_page`` in our ``views.py`` file.
-
-#. Add a declaration which maps the ``add_page`` route to the view named
- ``add_page`` in our ``views.py`` file.
-
-#. Add a declaration which maps the ``edit_page`` route to the view named
- ``edit_page`` in our ``views.py`` file.
+ route named ``edit_page``. This is the edit view for a page. It maps
+ to our ``edit_page`` view callable by virtue of the ``@view_config``
+ attached to the ``edit_page`` view function indicating
+ ``route_name='edit_page'``.
As a result of our edits, the ``__init__.py`` file should look
something like so:
View
148 docs/tutorials/wiki2/installation.rst
@@ -40,13 +40,6 @@ Preparation, UNIX
$ bin/easy_install pyramid
-#. Use ``easy_install`` to install various packages from PyPI.
-
- .. code-block:: text
-
- $ bin/easy_install docutils nose coverage zope.sqlalchemy \
- SQLAlchemy pyramid_tm
-
Preparation, Windows
--------------------
@@ -69,14 +62,6 @@ Preparation, Windows
c:\pyramidtut> Scripts\easy_install pyramid
-#. Use ``easy_install`` to install various packages from PyPI.
-
- .. code-block:: text
-
- c:\pyramidtut> Scripts\easy_install docutils \
- nose coverage zope.sqlalchemy SQLAlchemy pyramid_tm
-
-
.. _sql_making_a_project:
Making a Project
@@ -108,6 +93,13 @@ On Windows:
startup problems, try putting both the virtualenv and the project
into directories that do not contain spaces in their paths.
+Success executing this command will end with a line to the console something
+like::
+
+ Please run the "populate_tutorial" script to set up the SQL
+ database before starting the application (e.g.
+ "$myvirtualenv/bin/populate_tutorial development.ini".)
+
Installing the Project in "Development Mode"
============================================
@@ -131,6 +123,11 @@ On Windows:
c:\pyramidtut> cd tutorial
c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
+Success executing this command will end with a line to the console something
+like::
+
+ Finished processing dependencies for tutorial==0.0
+
.. _sql_running_tests:
Running the Tests
@@ -151,6 +148,14 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
+For a successful test run, you should see output like this::
+
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.094s
+
+ OK
+
Exposing Test Coverage Information
==================================
@@ -191,8 +196,24 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial ^
--cover-erase --with-coverage
-Looks like our package's ``models`` module doesn't quite have 100%
-test coverage.
+If successful, you will see output something like this::
+
+ .
+ Name Stmts Miss Cover Missing
+ ------------------------------------------------
+ tutorial 11 7 36% 9-15
+ tutorial.models 17 0 100%
+ tutorial.scripts 0 0 100%
+ tutorial.tests 24 0 100%
+ tutorial.views 6 0 100%
+ ------------------------------------------------
+ TOTAL 58 7 88%
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.459s
+
+ OK
+
+Looks like our package doesn't quite have 100% test coverage.
Starting the Application
========================
@@ -211,11 +232,96 @@ On Windows:
c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload
-Visit the Application in a Browser
-==================================
+If successful, you will see something like this on your console::
+
+ Starting subprocess with file monitor
+ Starting server in PID 8966.
+ Starting HTTP server on http://0.0.0.0:6543
+
+This means the server is ready to accept requests.
+
+Populating the Database
+=======================
+
+In a web browser, visit ``http://localhost:6543/``.
+
+You will see an error page with a title something like this::
+
+ sqlalchemy.exc.OperationalError
+
+ OperationalError: (OperationalError) no such table: models ...
+
+Oh no! Something isn't working!
+
+This happens because we haven't populated the SQL database with any table
+information yet. We need to use the ``populate_tutorial`` :term:`console
+script` to populate our database before we can see the page render correctly.
+
+Stop the running Pyramid application by pressing ``ctrl-C`` in the console.
+Make sure you're still in the ``tutorial`` directory (the directory with a
+``development.ini`` in it) and type the following command:
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/populate_tutorial development.ini
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\populate_tutorial development.ini
+
+The output to your console should be something like this::
+
+ 2011-11-26 14:42:25,012 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ PRAGMA table_info("models")
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ CREATE TABLE models (
+ id INTEGER NOT NULL,
+ name VARCHAR(255),
+ value INTEGER,
+ PRIMARY KEY (id),
+ UNIQUE (name)
+ )
+ 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2011-11-26 14:42:25,135 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+ 2011-11-26 14:42:25,137 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ BEGIN (implicit)
+ 2011-11-26 14:42:25,138 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ INSERT INTO models (name, value) VALUES (?, ?)
+ 2011-11-26 14:42:25,139 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ (u'one', 1)
+ 2011-11-26 14:42:25,140 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ COMMIT
+
+Success! You should now have a ``tutorial.db`` file in your current working
+directory. This will be a SQLite database with a single table defined in it
+(``models``).
+
+Starting the Application (Again)
+================================
+
+Start the application again.
+
+On UNIX:
+
+.. code-block:: text
+
+ $ ../bin/pserve development.ini --reload
+
+On Windows:
+
+.. code-block:: text
+
+ c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload
-In a browser, visit ``http://localhost:6543/``. You will see the
-generated application's default page.
+At this point, when you visit ``http://localhost:6543/`` in your web browser,
+you will no longer see an error; instead you will see the generated
+application's default page.
One thing you'll notice is the "debug toolbar" icon on right hand side of the
page. You can read more about the purpose of the icon at
View
3  docs/tutorials/wiki2/src/authorization/README.txt
@@ -1,4 +1 @@
tutorial README
-
-
-
View
8 docs/tutorials/wiki2/src/authorization/development.ini
@@ -1,5 +1,6 @@
[app:main]
use = egg:tutorial
+
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
@@ -19,7 +20,7 @@ port = 6543
# Begin logging configuration
[loggers]
-keys = root, sqlalchemy
+keys = root, tutorial, sqlalchemy
[handlers]
keys = console
@@ -31,6 +32,11 @@ keys = generic
level = INFO
handlers = console
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
[logger_sqlalchemy]
level = INFO
handlers =
View
1  docs/tutorials/wiki2/src/authorization/production.ini
@@ -1,5 +1,6 @@
[app:main]
use = egg:tutorial
+
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
View
3  docs/tutorials/wiki2/src/authorization/setup.py
@@ -42,6 +42,7 @@
entry_points = """\
[paste.app_factory]
main = tutorial:main
+ [console_scripts]
+ populate_tutorial = tutorial.scripts.populate:main
""",
)
-
View
25 docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -4,14 +4,15 @@
from sqlalchemy import engine_from_config
-from tutorial.models import initialize_sql
from tutorial.security import groupfinder
+from .models import DBSession
+
def main(global_config, **settings):
- """ This function returns a WSGI application.
+ """ This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
- initialize_sql(engine)
+ DBSession.configure(bind=engine)
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder)
authz_policy = ACLAuthorizationPolicy()
@@ -19,27 +20,13 @@ def main(global_config, **settings):
root_factory='tutorial.models.RootFactory',
authentication_policy=authn_policy,
authorization_policy=authz_policy)
- config.add_static_view('static', 'tutorial:static', cache_max_age=3600)
-
+ config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('login', '/login')
config.add_route('logout', '/logout')
config.add_route('view_page', '/{pagename}')
config.add_route('add_page', '/add_page/{pagename}')
config.add_route('edit_page', '/{pagename}/edit_page')
-
- config.add_view('tutorial.views.view_wiki', route_name='view_wiki')
- config.add_view('tutorial.login.login', route_name='login',
- renderer='tutorial:templates/login.pt')
- config.add_view('tutorial.login.logout', route_name='logout')
- config.add_view('tutorial.views.view_page', route_name='view_page',
- renderer='tutorial:templates/view.pt')
- config.add_view('tutorial.views.add_page', route_name='add_page',
- renderer='tutorial:templates/edit.pt', permission='edit')
- config.add_view('tutorial.views.edit_page', route_name='edit_page',
- renderer='tutorial:templates/edit.pt', permission='edit')
- config.add_view('tutorial.login.login',
- context='pyramid.httpexceptions.HTTPForbidden',
- renderer='tutorial:templates/login.pt')
+ config.scan()
return config.make_wsgi_app()
View
37 docs/tutorials/wiki2/src/authorization/tutorial/login.py
@@ -1,37 +0,0 @@
-from pyramid.httpexceptions import HTTPFound
-from pyramid.security import remember
-from pyramid.security import forget
-
-from tutorial.security import USERS
-
-def login(request):
- login_url = request.route_url('login')
- referrer = request.url
- if referrer == login_url:
- referrer = '/' # never use the 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(
- message = message,
- url = request.application_url + '/login',
- came_from = came_from,
- login = login,
- password = password,
- )
-
-def logout(request):
- headers = forget(request)
- return HTTPFound(location = request.route_url('view_wiki'),
- headers = headers)
-
View
37 docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -1,17 +1,20 @@
-import transaction
+from pyramid.security import (
+ Allow,
+ Everyone,
+ )
-from pyramid.security import Allow
-from pyramid.security import Everyone
+from sqlalchemy import (
+ Column,
+ Integer,
+ Text,
+ )
-from sqlalchemy import Column
-from sqlalchemy import Integer
-from sqlalchemy import Text
-
-from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import scoped_session
-from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm import (
+ scoped_session,
+ sessionmaker,
+ )
from zope.sqlalchemy import ZopeTransactionExtension
@@ -29,20 +32,6 @@ def __init__(self, name, data):
self.name = name
self.data = data
-def initialize_sql(engine):
- DBSession.configure(bind=engine)
- Base.metadata.bind = engine
- Base.metadata.create_all(engine)
- try:
- transaction.begin()
- session = DBSession()
- page = Page('FrontPage', 'This is the front page')
- session.add(page)
- transaction.commit()
- except IntegrityError:
- # already created
- transaction.abort()
-
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
(Allow, 'group:editors', 'edit') ]
View
1  docs/tutorials/wiki2/src/authorization/tutorial/scripts/__init__.py
@@ -0,0 +1 @@
+# package
View
35 docs/tutorials/wiki2/src/authorization/tutorial/scripts/populate.py
@@ -0,0 +1,35 @@
+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 <config_uri>\n'
+ '(example: "%s development.ini")' % (cmd, cmd))
+ sys.exit(1)
+
+def main(argv=sys.argv, settings=None):
+ 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('FrontPage', 'This is the front page')
+ DBSession.add(model)
View
1  docs/tutorials/wiki2/src/authorization/tutorial/security.py
@@ -5,4 +5,3 @@
def groupfinder(userid, request):
if userid in USERS:
return GROUPS.get(userid, [])
-
View
23 docs/tutorials/wiki2/src/authorization/tutorial/tests.py
@@ -1,15 +1,20 @@
import unittest
-
+import transaction
from pyramid import testing
def _initTestingDB():
- from tutorial.models import DBSession
- from tutorial.models import Base
from sqlalchemy import create_engine
+ from tutorial.models import (
+ DBSession,
+ Page,
+ Base
+ )
engine = create_engine('sqlite://')
- DBSession.configure(bind=engine)
- Base.metadata.bind = engine
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
def _registerRoutes(config):
@@ -20,14 +25,16 @@ def _registerRoutes(config):
class ViewWikiTests(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
+ self.session = _initTestingDB()
def tearDown(self):
+ self.session.remove()
testing.tearDown()
def _callFUT(self, request):
from tutorial.views import view_wiki
return view_wiki(request)
-
+
def test_it(self):
_registerRoutes(self.config)
request = testing.DummyRequest()
@@ -40,6 +47,7 @@ def setUp(self):
self.config = testing.setUp()
def tearDown(self):
+ self.session.remove()
testing.tearDown()
def _callFUT(self, request):
@@ -121,7 +129,8 @@ def test_it_notsubmitted(self):
self.session.add(page)
info = self._callFUT(request)
self.assertEqual(info['page'], page)
- self.assertEqual(info['save_url'], 'http://example.com/abc/edit_page')
+ self.assertEqual(info['save_url'],
+ 'http://example.com/abc/edit_page')
def test_it_submitted(self):
from tutorial.models import Page
View
76 docs/tutorials/wiki2/src/authorization/tutorial/views.py
@@ -1,20 +1,36 @@
import re
-
from docutils.core import publish_parts
-from pyramid.httpexceptions import HTTPFound, HTTPNotFound
-from pyramid.security import authenticated_userid
+from pyramid.httpexceptions import (
+ HTTPFound,
+ HTTPNotFound,
+ HTTPForbidden,
+ )
+
+from pyramid.view import view_config
+
+from pyramid.security import (
+ remember,
+ forget,
+ authenticated_userid,
+ )
-from tutorial.models import DBSession
-from tutorial.models import Page
+from .models import (
+ DBSession,
+ Page,
+ )
+
+from .security import USERS
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
+@view_config(route_name='view_wiki')
def view_wiki(request):
return HTTPFound(location = request.route_url('view_page',
pagename='FrontPage'))
+@view_config(route_name='view_page', renderer='templates/view.pt')
def view_page(request):
pagename = request.matchdict['pagename']
session = DBSession()
@@ -35,10 +51,11 @@ def check(match):
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
edit_url = request.route_url('edit_page', pagename=pagename)
- logged_in = authenticated_userid(request)
return dict(page=page, content=content, edit_url=edit_url,
- logged_in=logged_in)
+ logged_in=authenticated_userid(request))
+@view_config(route_name='add_page', renderer='templates/edit.pt',
+ permission='edit')
def add_page(request):
name = request.matchdict['pagename']
if 'form.submitted' in request.params:
@@ -50,9 +67,11 @@ def add_page(request):
pagename=name))
save_url = request.route_url('add_page', pagename=name)
page = Page('', '')
- logged_in = authenticated_userid(request)
- return dict(page=page, save_url=save_url, logged_in=logged_in)
+ return dict(page=page, save_url=save_url,
+ logged_in=authenticated_userid(request))
+@view_config(route_name='edit_page', renderer='templates/edit.pt',
+ permission='edit')
def edit_page(request):