12.2 Traversal

In a Web application the main user interface is the URI, for instance:

    http://localhost:8080/2007/05/;view_calendar

With itools.web a URI path is divided into two parts: the path and the method. The method is explicitly identified because it is preceded by the semicolon character. In this example:

The Path

Information is logically organized in a tree. In our example the tree would look like this:

    /
    |-- 2007
    |   |-- 01
    |   |-- 02
    |   |-- 03
    |   |-- 04
    |   |-- 05   <== the node at 2007/05
    |   |-- 06
    |   |-- 07
    |   |-- 08
    |   |-- 09
    |   |-- 10
    |   |-- 11
    |   \-- 12
    |-- 2008
    |   |-- 01
    ...

With itools.web all nodes in the tree are Python objects, instances of the class Resource (note that this is a base class, this is to say, it must be specialized).

The path (“2007/05” in our example) identifies a resource in the tree.

The method

Once we have the resource, the method (“view_calendar” in our example) will identify a method of that resource.

If the method is not explicitly specified, like in the URI:

    http://localhost:8080/2006/05

Then the method of the request (GET, HEAD, POST, etc.), will be used to determine which method of the resource will be called. So for instance if the request method is GET, then the resource method GET will be called.

Once we have the method, it will be called. And the value it returns will be used to build the response that the server will send to the client.

Traversal

In a word, by traversal we basically understand the process of:

  1. Picking the resource in the tree identified by the given path.

  2. Picking a method of this resource, either explicitly if specified in the URI, or implicitly.

  3. Calling the method.

12.2.1 Example: Calendar

To illustrate what has been explained so far, see this code:

    # Import from the Standard Library
    import calendar
    import datetime

    # Import from itools
    from itools.uri import get_reference
    from itools.web import Server, RootResource, Resource, BaseView

    class CalendarView(BaseView):
        access = True
        def GET(self, resource, context):
            month = int(resource.name)
            year = int(resource.parent.name)
            cal = calendar.month(year, month)
            return "<html><body><h2><pre>%s</pre></h2></body></html>" % cal

    class Month(Resource):
        view_calendar = CalendarView()


    class Year(Resource):
        def _get_resource(self, name):
            # Check the name is a valid month number
            try:
                month = int(name)
            except ValueError:
                raise LookupError
            if month < 1 or month > 12:
                raise LookupError
            return Month()


    class RootView(BaseView):
        access = True
        def GET(self, resource, context):
            today = datetime.date.today()
            path = today.strftime('%Y/%m/;view_calendar')
            return get_reference(path)

    class MyRoot(RootResource):
        default_view_name = 'root_view'
        root_view = RootView()

        def _get_resource(self, name):
            # Check the name is a valid year number
            try:
                year = int(name)
            except ValueError:
                raise LookupError
            if year < 1 or year > 9999:
                raise LookupError
            return Year()


    if __name__ == '__main__':
        root = MyRoot()
        server = Server(root)
        server.start()

To try this example type:

    $ python cal.py

Then go to the URL http://localhost:8080, and enjoy.

12.2.2 Basic programming interface

As the calendar example shows, with itools.web all nodes in the graph must be instances of the base class Resource. And all of them will have two attributes:

Based on these two attributes, the Resource class provides a rich API, here is an excerpt:

The namespace

Another important thing the example shows is the method _get_object. Our hierarchy of years and months is dynamically created, so we build objects to support traversal and drop them after the response is returned.

Return values

Something new in this example is the value returned by the RootView.GET method is not a byte string, but a itools.uri.Reference instance. The values a method can return are:

Most often these values will be enough for the programmer. If the response needs to be further modified, for example to send a different status code, or to add a response header, it is possible to directly manipulate the response object.