Tips for Writing Pylons Controller Actions

Author: Mike Orr
Date: 2006-12-07

QuickWiki actions

Here's quickwiki.controllers.page from the QuickWiki tutorial:

from quickwiki.lib.base import *
from quickwiki.lib.database import session_context

class PageController(BaseController):
    def index(self, title):
        page = self.query.get_by(title=title)
        if page:
            c.content = page.get_wiki_content()
            return render_response('/page.myt')
        elif model.wikiwords.match(title):
            return render_response('/new_page.myt')
        abort(404)

    def __before__(self):
        # This sets a SQLAlchemy *database* session, not a Web session.
        self.session = session_context.current
        self.query = self.session.query(model.Page)

    def edit(self, title):
        page = self.query.get_by(title=title)
        if page:
            c.content = page.content
        return render_response('/edit.myt')

    def save(self, title):
        page = self.query.get_by(title=title)
        if not page:
            page = model.Page()
            self.session.save(page)
            page.title = title
        page.content = request.params['content']
        c.title = page.title
        c.content = page.get_wiki_content()
        c.message = 'Successfully saved'
        self.session.flush()
        return render_response('/page.myt')

    def list(self):
        c.titles = [page.title for page in self.query.select()]
        return render_response('/titles.myt')

    def delete(self):
        title = request.params['id'][5:]
        page = self.query.get_by(title=title)
        self.session.delete(page)
        self.session.flush()
        c.titles = self.query.select()
        return render_response('/list.myt', fragment=True)

Controller methods ("actions") are tied to URLs by the Routes routing map, as described in Pylons Execution Analysis. In that article we clicked on "TitleList" in our Web browser and went to URL "/page/list", producing these routing variables:

{'action': 'list', 'controller': 'page', 'title': 'FrontPage'}

We see this method sets a c attribute and calls render_response('/titles.myt'). c is a special global used to pass data to the template or between methods; essentially a miscellaneous container for the request. render_response() fills in the template and returns a pylons.Response object.

The "from quickwiki.lib import *" line makes lots of goodies available. These are specific to the current request.

_(string)
Translate the argument into the user's preferred language using the translation tables in quickwiki.i18n.
abort

Abort the request immediately and raise an HTTPException. Usage:

abort(404)

You can raise an exception from paste.httpexceptions yourself, or see paste.httpexceptions.abort() for additional features.

c
The "context" global. Used to pass data to the templates or between methods.
cache
@@MO Something related to Pylons caching.
etag_cache
Manages browser caching ("304 Not Modified"). See pylons.helpers.etags_cache for usage.
g
The "globals" global. You can assign attributes in quickwiki.config.app_globals and they will be initialized at the start of the application and remain alive for every request until the application shuts down; thus it's a good place for shared external resources. By default it has only one attribute .pylons_config, the application configuration based on the config file that started it.
h
The "helpers" global, described in the "Helpers" section below.
jsonify

Decorator to convert a return value to JSON and wrap it in a Reponse with "Content-Type: text/javascript". Usage:

@jsonify
def my_controller_action(self):
    return {"variable_for_browser": "JSONify me!"}
model
Same as quickwiki.models.__init__.
redirect_to
Redirect the user to a different URL. This is the routes.util.redirect_to() function, which should be studied along with routes.util.url_for() for the usage and behavior.
render(relative_template_path)
Fill the specified template using data from the c global. This goes through pylons.templating, which provides a unified front end to several template engines (Myghty, Cheetah, Kid, etc).
render_response(relative_template_path)
Convenience method to fill a template and wrap the result in a Response in one step. Useful if the default response options are appropriate.
request

A paste.wsgiwrappers.WSGIRequest with information about the request. Frequently-used attributes:

.params
MultiDict of POST/GET parameters.
.environ
The WSGI environment dict.
.headers
Dict of incoming HTTP headers. Includes only the CGI "HTTP_*" headers. Access as "request-method" or "REQUEST_METHOD" (case insensitive) without the "HTTP_" prefix.
.cookies
Dict of incoming cookies.
.host, .scheme, .script_name, .path_info
URL parts.
.method
"GET" or "POST".
.body
The request body as a file-like object. Not usually necessary because .params processes POST vars for you.
rest

Functions for REST-ful applications. This is the pylons.decorators.rest module.

restrict

Decorator to restrict access to certain HTTP methods. The user will get a 405 error otherwise. Usage:

@rest.restrict("POST")
def my_post_action(self):
    ...
dispatch_to

Decorator to switch to a different action based on the HTTP method. If the HTTP method does not match any of the keyword args, continue with the original action. Usage:

@rest.dispatch_on(POST="create_comment")
def comment(self):
    # Display the comment.

def create_comment(self):
    # Create the comment.
Response
You must return an instance of this.
session
A dict-like object that is persistent between requests during this browser session with this user. You can store anything in it.
validate
Decorator to validate input from a FormEncode schema or from individual validators. See pylons.decorators.__init__ for usage and behavior.

c, g, h, and pylons.request are special globals containing information about the current request and the server environment. c is the "context" object, used to pass data to the template or between methods. Initially it contains some of the routing variables:

print vars(c._current_obj())
{'titles': [u'Front Page']}

g is the "global" object. By default it has one attribute .pylons_config containing the configuration. You can add other attributes -- anything that's initialized at application startup and remains active through all requests -- in quickwiki.lib.app_globals.

Helpers

h is the quickwiki.lib.helpers module. It contains generic functions useful in Web applications, including all the WebHelpers ported from Ruby on Rails. Here are some of the more interesting ones included by default:

From pylons.util

log
Log a message to the server's error log.
set_lang
Set the user's preferred language.
_
Translate the given string into the preferred language using the gettext translations you've put in quickwiki.i18n.

Seeing the global values

To see what the global variables c, g, and h contain during each request, change quickwiki.lib.base.BaseController.__call__() to:

#return WSGIController.__call__(self, environ, start_response)
print environ["pylons.routes_dict"]
result = WSGIController.__call__(self, environ, start_response)
import pprint
print "c is", pprint.pformat(vars(c._current_obj()))
print "g is", pprint.pformat(vars(g._current_obj()))
print "g.pylons_config is", pprint.pformat(vars(g.pylons_config))
print "h dir is", dir(h)
return result

You will probably want to quickly comment out some of these lines because the output is pretty verbose. (c and g are actually proxy objects, so attribute access is implicitly passed down to the real object but less common operations like vars() require the ._current_obj() call.)