This HOWTO documents the Controller objects, part of the Woven framework. The Woven framework should not be used for new projects. The newer Nevow framework, available as part of the Quotient project, is a simpler framework with consistent semantics and better testing and is strongly recommended over Woven.
The Woven documentation below is maintained only for users with an existing Woven codebase.

Controller objects are a way to generalize and reuse input handling logic. In Twisted Web, form input is passed to a Resource instance in request.args. You can create controller classes to encapsulate generic request.args handling, and perform validation and Model updating tasks.
Main Concepts
- Controller factories provide the glue from a DOM node with a 'controller=' directive to an instance of a Controller class.
- handle is the method which is called on the Controller instance to handle a node.
- InputHandlers are Controllers which have (somewhat) convenient syntax for handling a node.
- Event handlers, when used with LivePage, are a brain-exploding way of handling JavaScript events in your pages with server-side Python code.
Controller factories
Controller factories provide the glue between a controller=
directive on a DOM node and a Controller instance. When a DOM node with a
controller=
directive is encountered, Woven looks for a
corresponding wcfactory_
method on your Page instance. A Controller
factory is required to return an object which implements the interface IController
.
class MyController(controller.Controller): pass class MyPage(page.Page): def wcfactory_foo(self, request, node, model): return MyController(model)
Handle
Handle is the API your controller must implement to handle a node. It's return value may be a Deferred if you wish to pause the rendering of the View until some data is ready, or it may be None
class MyController(controller.Controller): def handle(self, request, node): name = request.args.get("name", [None])[0] print "HOORJ! YOUR NAME IS %s" % name
InputHandlers
InputHandlers are defined in woven.input
. They were an early attempt to create a
class which made it easy to create new input validators and input committers. It
is usable in its current state, although the API is a bit baroque. Subclasses of
input.InputHandler
can
override the following methods to decide what to do with data
initialize()
- initialize this Controller. This is most useful for registering event handlers on the View with addEventHandler, discussed below.
getInput
(self, request)
- get input from the request and return it. Return None to indicate no data was available for this InputHandler to handle.
check
(self, request, data)
- Check the input returned from getInput and return:
- None if no data was submitted (data was None), or
- True if the data that was submitted was valid, or
- False if the data that was submitted was not valid.
handleValid
(self, request, data)
- handle the valid submission of some data. By default this calls
self.parent.aggregateValid
. aggregateValid
(self, request, inputhandler, data)
- Some input was validated by a child Controller. This is generally
implemented on a controller which is placed on a
<form>
to gather input from controllers placed on<input>
nodes. handleInvalid
(self, request, data)
- handle the invalid submission of some data. By default this calls
self.parent.aggregateInvalid
. aggregateInvalid
(self, request, inputhandler, data)
- Some input was declared invalid by a child Controller. This is generally
implemented on a controller which is placed on a
<form>
to gather input from controllers placed on<input>
nodes. commit
(self, request, node, data)
- Enough valid input was gathered to allow us to change the Model.
InputHandlers have been parameterized enough so you may simply use a generic InputHandler rather than subclassing and overriding:
class MyPage(page.Page): def checkName(self, request, name): if name is None: return None # No fred allowed if name == 'fred': return False return True def commitName(self, request, name=""): ctx = getContext() ctx.execute("insert into people (name) values %s", name) def wcfactory_addPerson(self, request, node, model): return input.InputHandler( model, name="name", # The name of the argument in the request to check check=self.checkName, commit=self.commitName)
Event handlers
In order for Event Handlers to work, you must be using LivePage, and include the webConduitGlue View in your HTML template.
Event handlers give you the powerful ability to respond to in-browser JavaScript event handlers with server-side Python code. Event handlers are registered on the View instance; in some cases, it may make most sense for your View instances to implement their own event handlers. However, in order to support good separation of concerns and code reuse, you may want to consider implementing your event handlers on a Controller instance.
The easiest way to achieve this is to subclass input.Anything
(XXX: this
should just be controller.Controller) and override initialize
(XXX: this should
be setUp):
class MyEventHandler(input.Anything): def initialize(self): # haaa self.view.addEventHandler("onclick", self.onClick) self.view.addEventHandler("onmouseover", self.onMouseOver, "'HELLO'") def onClick(self, request, widget): print self, "CLICKED!!!" def onMouseOver(self, request, widget, argument): print self, "MOUSE OVER!!!", argument
Note that the first argument to addEventHandler is the JavaScript event name, and the second argument is the python function or method which will handle this event. You may also pass any additional arguments you desire. These arguments must be valid JavaScript, and will be evaluated in the browser context. The results of these JavaScript expressions will be passed to your Python event handler.
Note that when we passed an extra argument when adding an
onmouseover
event handler, we passed a string enclosed in two sets
of quotes. This is because the result of evaluating "'HELLO'"
as
JavaScript in the browser is the string 'HELLO'
, which is then
passed to the Python event handler. If we had simply passed "HELLO"
to addEventHandler, Woven would have evaluated "HELLO"
in the
browser context, resulting in an error because the variable HELLO
is not defined.
Any normal client-side JavaScript object may be accessed, such as
document
and window
. Also, the JavaScript variable
node
is defined as the DOM node on which the event handler is
operating. This is useful for examining the current value of an
<input>
node.
Here are some examples of useful Event handlers:
class Redirect(input.Anything): def initialize(self): #heee self.view.addEventHandler( "onclick", self.onClick, "window.location = 'http://www.google.com'") def onClick(self, request, widget, arg): print "The window was redirected." class OnChanger(input.Anything): def initialize(self): #hooo self.view.addEventHandler( "onchange", self.changed, "node.value") def changed(self, request, widget, newValue): print "The input box changed to", newValue