Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Finish the Pyramid for Pylons Users guide.

  • Loading branch information...
commit 3d5936fc9dc170459ab3ff722668a1bb41fe8e1a 1 parent 48f7568
@mikeorr mikeorr authored
4 links.rst
@@ -34,3 +34,7 @@
.. _Deform:
.. _pyramid_simpleform:
+.. _Kotti:
+.. _Ptah:
+.. _Khufu:
160 pylons/auth.rst
@@ -0,0 +1,160 @@
+Authentication and Authorization
+*This chapter is contributed by Eric Rasmussen.*
+Pyramid has built-in authentication and authorization capibalities that make it
+easy to restrict handler actions. Here is an overview of the steps you'll
+generally need to take:
+1) Create a root factory in your model that associates allow/deny directives
+ with groups and permissions
+2) Create users and groups in your model
+3) Create a callback function to retrieve a list of groups a user is subscribed to based on their user ID
+4) Make a "forbidden view" that will be invoked when a Forbidden exception is
+ raised.
+5) Create a login action that will check the username/password and remember the
+ user if successful
+6) Restrict access to handler actions by passing in a
+ permission='somepermission' argument to ``@view_config``.
+7) Wire it all together in your config
+You can get started by adding an import statement and custom root factory to
+your model::
+ from import Allow, Everyone
+ class RootFactory(object):
+ __acl__ = [ (Allow, Everyone, "everybody"),
+ (Allow, "basic", "entry"),
+ (Allow, "secured", ("entry", "topsecret"))
+ ]
+ def __init__(self, request):
+ pass
+The custom root factory generates objects that will be used as the context of
+requests sent to your web application. The first attribute of the root factory
+is the ACL, or access control list. It's a list of tuples that contain a
+directive to handle the request (such as Allow or Deny), the group that is
+granted or denied access to the resource, and a permission (or optionally a
+tuple of permissions) to be associated with that group.
+The example access control list above indicates that we will allow everyone to
+view pages with the 'everybody' permission, members of the basic group to view
+pages restricted with the 'entry' permission, and members of the secured group
+to view pages restricted with either the 'entry' or 'topsecret' permissions.
+The special principal 'Everyone' is a built-in feature that allows any person
+visiting your site (known as a principal) access to a given resource.
+For a user to login, you can create a handler that validates the login and
+password (or any additional criteria) submitted through a form. You'll
+typically want to add the following imports::
+ from pyramid.httpexceptions import HTTPFound
+ from import remember, forget
+Once you validate a user's login and password against the model, you can set
+the headers to "remember" the user's ID, and then you can redirect the user to
+the home page or url they were trying to access::
+ # retrieve the userid from the model on valid login
+ headers = remember(self.request, userid)
+ return HTTPFound(location=someurl, headers=headers)
+Note that in the call to the remember function, we're passing in the user ID we
+retrieved from the database and stored in the variable 'userid' (an arbitrary
+name used here as an example). However, you could just as easily pass in a
+username or other unique identifier. Whatever you decide to "remember" is what
+will be passed to the groupfinder callback function that returns a list of
+groups a user belongs to. If you import ``authenticated_userid``, which is a
+useful way to retrieve user information in a handler action, it will return the
+information you set the headers to "remember".
+To log a user out, you "forget" them, and use HTTPFound to redirect to another
+ headers = forget(self.request)
+ return HTTPFound(location=someurl, headers=headers)
+Before you restrict a handler action with a permission, you will need a
+callback function to return a list of groups that a user ID belongs to. Here is
+one way to implement it in your model, in this case assuming you have a Groups
+object with a groupname attribute and a Users object with a mygroups relation
+to Groups::
+ def groupfinder(userid, request):
+ user = Users.by_id(userid)
+ return [g.groupname for g in user.mygroups]
+As an example, you could now import and use the @action decorator to restrict
+by permission, and authenticated_userid to retrieve the user's ID from the
+ from pyramid_handlers import action
+ from import authenticated_userid
+ from models import Users
+ class MainHandler(object):
+ def __init__(self, request):
+ self.request = request
+ @action(renderer="welcome.html", permission="entry")
+ def index(self):
+ userid = authenticated_userid(self.request)
+ user = Users.by_id(userid)
+ username = user.username
+ return {"currentuser": username}
+This gives us a very simple way to restrict handler actions and also obtain
+information about the user. This example assumes we have a Users class with a
+convenience class method called by_id to return the user object. You can then
+access any of the object's attributes defined in your model (such as username,
+email address, etc.), and pass those to a template as dictionary key/values in
+your return statement.
+If you would like a specific handler action to be called when a forbidden
+exception is raised, you need to add a forbidden view. This was covered
+earlier, but for completelness::
+ @view_config(renderer='myapp:templates/forbidden.html',
+ context='pyramid.exceptions.Forbidden')
+ @action(renderer='forbidden.html')
+ def forbidden(request):
+ ...
+The last step is to configure to use your auth policy. Make sure to
+add these imports::
+ from pyramid.authentication import AuthTktAuthenticationPolicy
+ from pyramid.authorization import ACLAuthorizationPolicy
+ from .models import groupfinder
+In your main function you'll want to define your auth policies so you can
+include them in the call to Configurator::
+ authn_policy = AuthTktAuthenticationPolicy('secretstring',
+ callback=groupfinder)
+ authz_policy = ACLAuthorizationPolicy()
+ config = Configurator(settings=settings,
+ root_factory='myapp.models.RootFactory',
+ authentication_policy=authn_policy,
+ authorization_policy=authz_policy)
+ config.scan()
+The capabilities for authentication and authorization in Pyramid are very easy
+to get started with compared to using Pylons and repoze.what. The advantage is
+easier to maintain code and built-in methods to handle common tasks like
+remembering or forgetting users, setting permissions, and easily modifying the
+groupfinder callback to work with your model. For cases where it's manageable
+to set permissions in advance in your root factory and restrict individual
+handler actions, this is by far the simplest way to get up and running while
+still offering robust user and group management capabilities through your
+However, if your application requires the ability to create/edit/delete
+permissions (not just access through group membership), or you require the use
+of advanced predicates, you can either build your own auth system (see the
+Pyramid docs for details) or integrate an existing system like repoze.what.
+You can also use "repoze.who" with Pyramid's authorization system if you want to
+use Who's authenticators and configuration.
12 pylons/deployment.rst
@@ -0,0 +1,12 @@
+Deployment is the same for Pyramid as for Pylons. Specify the desired WSGI
+server in the "[server:main]" and run "pserve" with it. The default server in
+Pyramid is Waitress, compared to PasteHTTPServer in Pylons.
+Waitress' advantage is that it runs on Python 3. Its disadvantage is that it
+doesn't seek and destroy stuck threads like PasteHTTPServer does. If you're
+like me, that's enough reason not to use Waitress in production. You can switch
+to PasteHTTPServer or CherryPy server if you wish, or use a method like
+mod_wsgi that doesn't require a Python HTTP server.
8 pylons/index.rst
@@ -1,13 +1,11 @@
Pyramid for Pylons Users
-:Updated: 2012-04-30
+:Updated: 2012-06-12
:Versions: Pyramid 1.3
:Author: Mike Orr
-*XXX This guide is under construction....*
This guide discusses how Pyramid 1.3 differs from Pylons 1, and a few ways to
make it more like Pylons. The guide may also be helpful to readers coming from
Django or another Rails-like framework. The author has been a Pylons developer
@@ -36,8 +34,10 @@ skim through the rest of the manual to see which sections cover which topics.
+ deployment
+ auth
+ other
- unfinished
.. include:: ../links.rst
41 pylons/migrate.rst
@@ -1,17 +1,38 @@
Migrating an Existing Pylons Application
-If you're upgrading a large Pylons application to Pyramid, you can do it one
-route at a time and have it fall back to the Pylons application for URLs which
-don't exist. There are a few ways to set this up. One is to use Paste Cascade
--- the same way Pylons serves static files. In the INI file, make the main
-application ``paste.cascade.Cascade``, delegating to the Pyramid application
-first and falling back to the Pylons app.
+There are two general ways to port a Pylons application to Pyramid. One is to
+start from scratch, expressing the application's behavior in Pyramid. Many
+aspects such as the models, templates, and static files can be used unchanged
+or mostly unchanged. Other aspects like such as the controllers and globals
+will have to be rewritten. The route map can be ported to the new syntax, or
+you can take the opportunity to restructure your routes.
-Another way is to wrap the Pylons application in a Pyramid view. See
-`pyramid.wsgiapp.wsgiapp2`_ and `Porting an Existing WSGI Application to
+The other way is to port one URL at a time, and let Pyramid serve the ported
+URLs and Pylons serve the unported URLs. There are several ways to do this:
+* Run both the Pyramid and Python applications in Apache, and use mod_rewrite
+ to send different URLs to different applications.
+* Set up ``paste.cascade`` in the INI file, so that it will first try one
+ application and then the other if the URL returns "Not Found". (This is how
+ Pylons serves static files.)
+* Wrap the Pylons application in a Pyramid view. See pyramid.wsgiapp.wsgiapp2_.
+Also see the `Porting Applications to Pyramid`_ section in the Cookbook.
+*Caution:* running a Pyramid and a Pylons application simultaneously may bring up
+some tricky issues such as coordiating database connections, sessions, data
+files, etc. These are beyond the scope of this Guide.
+You'll also have to choose whether to write the Pyramid application in Python 2
+or 3. Pyramid 1.3 runs on Python 3, along with Mako and SQLAlchemy, and the
+Waitress and CherryPy HTTP servers (but not PasteHTTPServer). But not all
+optional libraries have been ported yet, and your application may depend on
+libraries which haven't been.
+.. include:: ../links.rst
.. _pyramid.wsgiapp.wsgiapp2:
-.. _Porting an Existing WSGI Application to Pyramid:
+.. _Porting Applications to Pyramid: ../porting/index.html
186 pylons/other.rst
@@ -0,0 +1,186 @@
+Other Pyramid Features
+Pyramid has a command to preload your application into an interactive Python
+prompt. This can be useful for debugging or experimentation. The command is
+"pshell", akin to "paster shell" in Pylons.
+.. code-block:: sh
+ $ pshell development.ini
+ Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32)
+ [GCC 4.4.3] on linux2
+ Type "help" for more information.
+ Environment:
+ app The WSGI application.
+ registry Active Pyramid registry.
+ request Active request object.
+ root Root of the default resource tree.
+ root_factory Default root factory used to create `root`.
+ >>>
+It doesn't initialize quite as many globals as Pylons, but ``app`` and
+``request`` will be the most useful.
+Other commands
+Other commands available:
+* proutes: list the application's routes. (Akin to Pylons "paster routes".)
+* pviews: list the application's views.
+* ptweens: list the application's tweens.
+* prequest: load the application, process a specified URL, and print the
+ response body on standard output.
+Pyramid does not include a form library. Pylons includes WebHelpers for form
+generation and FormEncode for validation and error messages. These work under
+Pyramid too. However, there's no built-in equivalent to Pylons' ``@validate``
+decorator. Instead we recommend the "pyramid_simpleform_" package, which
+replaces @validate with a more flexible structure.
+There are several other form libraries people use with Pyramid. These are
+discussed in the regular Forms_ section in the Pyramid Cookbook.
+WebHelpers is a third-party package containing HTML tag builders, text
+functions, number formatting and statistical functions, and other generic
+functions useful in templates and views. It's a Pylons dependency but is
+optional in Pyramid.
+The ``webhelpers.pylonslib`` subpackage does not work with Pyramid because it
+depends on Pylons' special globals. ``webhelpers.mimehelper`` and
+``webhelpers.paginate`` have Pylons-specific features that are disabled under
+other frameworks. WebHelpers has not been tested on Python 3.
+The next version of WebHelpers may be released as a different distribution
+(WebHelpers2) with a subset of the current helpers ported to Python 3. It will
+probably spin off Paginate and the Feed Generator to separate distribitions.
+The events framework provides hooks where you can insert your own code into the
+request-processing sequence, similar to how Apache modules work. It standarizes
+some customizations that were provided ad-hoc in Pylons or not at all. To use
+it, write a callback function for one of the event types in ````:
+``ApplicationCreated``, ``ContextFound``, ``NewResponse``, ``BeforeRender``.
+The callback takes an event argument which is specific to the event type.
+You can register the event with ``@asubscriber`` or
+``config.add_subscriber()``. The Akhet demo has examples.
+For more details see:
+* `Using Events * <>`_
+* `Using The Before Render Event <>`_
+* `pyramid.event API <>`_
+URL generation
+Pyramid does not come with a URL generator equivalent to "pylons.url".
+Individual methods are available on the Request object to generate specific
+kinds of URLs. Of these, route_url covers the normal case of generating a route
+by name::
+ request.route_url("route_name", variable1="value1")
+ request.route_path("route_name", variable1="value1")
+ request.route_url("search", _query={"q": "search term"}, _anchor="results")
+As with all the \*_url vs \*_path methods, ``route_url`` generates an absolute
+URL, while ``route_path`` generates a "slash" URL (without the scheme or host).
+The ``_query`` argument is a dict of query parameters (or a sequence of
+key-value pairs). The ``_anchor`` argument makes a URL with a "#results"
+fragment. Other special keyword arguments are ``_scheme``, ``_host``,
+``_port``, and ``_app_url``.
+The advantage of using these methods rather than hardcoding the URL, is that it
+automatically addds the application prefix (which may be something more than
+"/" if the application is mounted on a sub-URL).
+You can also pass additional positional arguments, and they will be appended
+to the URL as components. This is not very useful with URL dispatch, it's more
+of a traversal thing.
+If the route is defined with a *pregenerator*, it will be called with the
+positional and keyword arguments, and can modify them before the URL is
+Akhet has a URLGenerator class, which you can use as shown in the Akhet demo to
+make a ``url`` variable for your templates, using an event subscriber. Then you
+can do things like this::
+ url.route("route_name") # Generate URL by route name.
+ url("route_name") # The same.
+ # The application's top-level URL.
+ url.current() # The current request URL. (Used to
+ # link to the same URL with different
+ # match variables or query params.)
+You can also customize it to do things like this::
+ url.static("images/logo.png")
+ url.image("logo.png") # Serve an image from the images dir.
+ url.deform("...") # Static file in the Deform package.
+If "url" is too long for you, you can even name it "u"!
+Utility scripts
+Pyramid has a documented way to write utility scripts for maintenance and the
+like. See `Writing a Script`_.
+Pyramid makes it easier to write unit tests for your views.
+(XXX Need a comparison example.)
+Pyramid has support for internationalization. At this time it's documented
+mainly for Chameleon templates, not Mako.
+Higher-level frameworks
+Pyramid provides a flexible foundation to build higher-level frameworks on.
+Several have already been written. There are also application scaffolds and
+* Kotti_ is a content management system that both works out of the box and can
+ be extended.
+* Ptah_ is a framework that aims to have as many features as Django. (But no
+ ponies, and no cowbells.) It has a minimal CMS component.
+* Khufu_ is a suite of scaffolds and utilities for Pyramid.
+* The Akhet_ demo we have mentioned before. It's a working application in a
+ tarball that you can copy code from.
+At the opposite extreme, you can make a tiny Pyramid application in 14 lines of
+Python without a scaffold. The Pyramid manual has an example: `Hello World`_.
+This is not possible with Pylons -- at least, not without distorting it
+.. include:: ../links.rst
+.. _Forms: ../forms/index.html
+.. _Writing a Script:
+.. _Hello World:
22 pylons/unfinished.rst
@@ -1,22 +0,0 @@
-Unfinished notes
-static files.
-Advanced views:
-- context
-- exception views: not found, forbidden, custom exceptions (validation failed)
-- add_slash argument to config.add_notfound_view.
-- traversal
-- traversal under a route
-- root factory under a route (without traverse)
-Deployment settings in INI file vs application settings in main function.
-Deploying an application with an alternate server, and from a top-level script
-without PasteDeploy.
-Testing. Pyramid maks it easier to write unit tess than Pylons. Examples.
Please sign in to comment.
Something went wrong with that request. Please try again.