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.

Woven

Introduction

Woven is a Web Application Framework for building highly interactive web applications, written in Python. It separates HTML Templates from page-generation logic written in Python, and uses the Model View Controller (MVC) pattern to create dynamic HTML on the fly. Woven is higher level framework that depends on the Twisted Web package of the Twisted Framework.

Twisted Overview

Twisted is a framework written in Python for building network applications. A core design feature of Twisted is its asynchronous networking nature. Because of the high overhead and locking requirements of threads, Twisted chooses to instead use the highly efficient network event notification mechanisms provided by the OS in the form of the C poll() or select() calls. Twisted uses the Reactor pattern, which is an event-loop style of programming which facilitates asynchronous programming.

Asynchronous programming requires breaking your program into smaller function chunks that trigger an operation which may potentially take a long time (for example, a network request) and return. In order to continue the flow of your code when the operation has completed, you must register a callback function which will be called with the resulting data when it is ready. Twisted includes a generalized callback handling mechanism, Deferred, discussed in the Deferred execution paper.

However, since writing Woven applications already involves breaking your functions into small, reusable Model-View-Controller components, Woven is able to handle the asynchronous nature of Twisted for you. By writing Models which provide data, potentially asynchronously, and Views which render data when it is ready, you are doing all that is required to act as a good citizen within the Twisted framework.

Twisted Web Object Publishing and Woven

Twisted includes a Web Server that handles HTTP requests and returns dynamic pages. It is useful when used in conjunction with Apache for serving static files such as Images. Apache can be set up to forward a specific URL via ProxyPass to the Twisted Web server. Twisted Web uses the concept of Object Publishing, similar to Zope, where there is a root Python Object below which all other Objects representing URLs are located.

When a request comes in to Twisted Web, Twisted Web splits the URL into segments and begins looking for Objects below the root by calling getChild tail-recursively. For example, if the URL http://example.com/foo/bar/baz is requested, Twisted splits this into the list of path segments ['foo', 'bar', 'baz']. It then calls root.getChild('foo'), calls getChild('bar') on the result, calls getChild('baz') on the second result, and finally calls render(request) on the result of the final getChild call.

For more details about Twisted Web, see Overview of Twisted Web.

Smalltalk Model-View-Controller Overview

Originally developed for Smalltalk, the MVC design pattern is a flexible mechanism for creating both GUI and web application user interfaces. The primary advantage of the MVC pattern is separation of business logic from presentation details and provides a loose coupling between an application's Model (state) and View (presentation). All of this makes code reuse easier and enables a division of labor between application design and user interface design, albeit at the expense of a little extra work.

A Model is an object that represents or encapsulates an application's business logic or state. The model contains both data and business logic code, but does not contain presentation or rendering code.

A View is an object that contains presentation or rendering code, but does not contain business logic code.

Finally, a Controller is a dispatcher object that mediates flow between the Model and the View. In traditional Smalltalk MVC, the Controller is responsible for polling the Mouse and Keyboard and converting the user's actions (Click a button, for example) into high-level events (Change the Model data from 0 to 1, and redraw a View which represents this Model to the user).

Woven Model-View-Controller Overview

In Woven, a Model is a python object that holds the data that will eventually be rendered by a view object into a portion of an HTML page. Woven Models may be any Python object; Woven accomplishes this using IModel Adapters:Components. Since different Python objects may act as Containers using different semantics, IModel adapters are required to allow Woven to address all Container objects uniformly. For example, Dictionaries are indexed using string keys. Lists are indexed using integer keys. Objects provide references to other objects using dot syntax (foo.bar), not square bracket syntax (foo['bar']). IModel Adapters are provided for all the basic Python types, Dictionaries, Lists, Integers, Floats, Strings, and Instances, and work behind the scenes to provide Model data to your Pages.

In Woven, a View is comprised of a Page object and many Widget objects. A Page object loads a template file (an XHTML file) that contains references to Widget objects (python objects that replace specific portions of the DOM created from the XHTML file). A single XHTML template references one Page object, but many Widget objects.

Widgets come in two flavors: local and global. Local Widgets are specific to only one template file, because the logic they perform is very specific, while global Widgets are general enough to replace placeholder content in many template files. Local Widgets are defined on the Page class, by defining a method with the prefix wvupdate_. Global Widgets are defined as subclasses of the Widget class.

In Woven, Page objects act as the default Controller object, similar to Servlets in Struts. ((please explain further?)) Since the Web is Request-Response and not event-driven like a Smalltalk application, the most basic event to be handled from the user is URL Requested. The root Page object contains logic for locating the correct Page object to handle the request. Once this object is located, it handles the url requested event by rendering itself into HTML. Thus, the Page object acts as both the main View and Controller for this event.

However, unlike Struts, Woven also supports more Smalltalk-like Controller programming. With a Controller, it is possible to register a Python event handling function which will be called when a specific JavaScript event occurs in the Browser. These events are relayed to the server out-of-band (without causing the main page to refresh) and can be handled asynchronously. Thus, it is possible to program Web Applications which act more like traditional Smalltalk Desktop Applications.

Often in Woven, it is convenient for a single object to have both the View and Controller responsibilities, though this is not strictly necessary. It is only useful to split out the Controller logic from the View if the request argument handling is general enough to be reusable across multiple pages.

Overview of Woven Main Concepts

In Depth Pages about Woven components

Templates

Templates in Woven are XHTML documents, marked up with four special woven attributes:

HTML Template example

  <html>
    <body>
      <h3 model="name" view="Text" />
      <div model="stuff" view="List">
        <span pattern="listItem" view="Text" />
      </div>
    </body>
  </html>

Models

Model objects are arranged in a tree. Each Woven page has a model tree with exactly one root. All data required for display by a Woven page is made available through named sub-models below this root.

Any Python object may be used as a Woven model.

An example of a model tree built using simple python data structures, Dictionaries, Lists, and Strings:

    model = {
      'name': 'Welcome to Woven',
      'stuff': [
        'Foo',
        'Bar',
        'Baz'
      ]
    }

Each model in this tree has a submodel path which we can use to refer to it.

Views

View objects are constructed each time a view= directive is encountered.

If view="Text" is specified on a Node, an instance of the Text class will be constructed to handle the node.

An example View widget which simply inserts the model data into the DOM as a text node:

class Text(view.View):
    def generate(self, request, node, model):
        data = str(model.getData())
        newTextNode = request.d.createTextNode(data)
        node.appendChild(newTextNode)
        return node

The node that is returned from the generate method replaces the template node in the final HTML output.

In the above case, the same node that was passed in was returned, after being changed (mutated).

The View object should return a DOM node that has been populated with the given model data.

Controllers

Controllers are responsible for accepting input from the user and making appropriate changes to the model.

They are also responsible for ensuring that the data submitted by the user is valid before changing the model.

Very few applications need the flexibility of separate Controller objects. Often, is is more convenient and clear to place the Controller logic in the View, where the View can make sure it has the latest data before rendering itself.

An example of a Controller which verifies the user's input before committing it to the Model:

class NewName:
    def handle(self, request):
        newName = request.args.get("newname", None)
        if newName is None:
          ## The user did not submit the form; do not do anything.
          return
        if newName == "":
          self.view.setError("Sorry, you didn't enter a name.")
        else:
          self.model.setData(newName)
          ## Tell the model that we are done making changes to it, 
          ## and Views that rely upon this model should rerender.
          self.model.notify({'request': request})

Pages

Pages are the entry point into a Woven application. The Page object accepts a request to render a web page from twisted.web and drives the page rendering process by parsing the template, locating Model objects in the tree, and invoking Controller and View objects.

Page is a subclass of Resource, the twisted.web class representing an individual URL. Resource instances can be hooked up to the twisted.web HTTP server in several ways. The simplest way to hook up a Resource to a web URL is to start a static twisted.web server, which will serve files out of the given directory:

% mktap web --path /Users/dsp/Sites
% twistd -nf web.tap

If you visit the URL http://localhost:8080/, you will see the contents of the directory you specified. To create a URL which will be served by an instance of Page, create a Python script. In this script, instantiate a Page instance, passing it a Model and a Template, and assign it to a variable named resource:

from twisted.web.woven import page

model = {'name': 'Welcome to Woven', 
         'stuff': ['Foo', 'Bar', 'Baz']}
  
template = """<html>
  <body>
      <h3 model="name" view="Text" />
      <div model="stuff" view="List">
        <p pattern="listItem" view="Text" />
      </div>
    </body>
  </html>
"""

resource = page.Page(model, template=template)

Name this script test.rpy and place it in the directory served by twisted.web. Then visit the URL http://localhost:8080/test.rpy with your web browser, and you should see a page with the HTML-formatted model data.

Page rendering process

When Woven renders a page, it first constructs a DOM (Document Object Model) which represents the template in memory using Python objects.

Woven then examines the DOM using a pre-order traversal looking for nodes with woven directives (nodes with model=, view=, or controller= attributes).

When a directive node is encountered, Woven locates/constructs Model, View, and Controller objects to handle this node.

The Controller is then triggered by calling handle. The controller may take any action required of it to update the Model data, or may do nothing.

The View is then rendered by calling generate. The View object is passed a DOM node and a reference to the Model. The view then manipulates the DOM node, placing the Model data in it.

The DOM returned from the View is then traversed, looking for directives to handle.

When the entire DOM has been traversed and mutated, the DOM is converted to HTML and sent to the browser.

Further Reading

Twisted Web is the Object-publishing web server woven uses to glue HTTP requests to Page rendering.

Page objects are the IResource implementors in the Woven framework. They represent URL segments and drive the Woven template rendering process. They also contain convenient methods for specifying Page trees.

Model objects provide data to Woven pages. Woven includes IModel adapters for the basic Python types and makes it easy for you to write your own Model classes and IModel adapters for your already-existing classes.

View objects insert Model data into the DOM provided by the Template. They use DOM syntax for HTML generation, and have convenient syntax for locating and copying pattern= nodes abstractly.

Controller objects accept input from the request and update the Model data. Controller objects are optional; often your Page or View instances can contain the controller logic and still make sense. Controllers are for cases which are general enough to warrant validation and commit logic.

LivePage objects allow you to build DHTML behaviors into your Woven pages using pure server-side Python. It includes code for asynchronously forwarding client-side JavaScript events to the server without refreshing the page, and sending JavaScript to a page from the server after it has already been loaded.

Form is a woven module that makes it easy to create HTML forms from existing Python methods which take keyword arguments. It also supports basic input validation.

Guard is a woven module that allows you to wrap your Page instances with login pages, to prevent unauthorized users from accessing them.