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 is “2007/05”.
The method is “view_calendar”.
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 Node (note that this is a base class, this is to say, it must be specialized).
The path (“2007/05” in our example) identifies a node in the tree.
Once we have the node, the method (“view_calendar” in our example) will identify a method of that node.
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 node will be called. So for instance if the request method is GET, then the node 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.
In a word, by traversal we basically understand the process of:
Picking the node in the tree identified by the given path.
Picking a method of this node, either explicitly if specified in the URI, or implicitly.
Calling the method.
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, Root, Node
class Month(Node):
view_calendar__access__ = True
def view_calendar(self, context):
month = int(self.name)
year = int(self.parent.name)
cal = calendar.month(year, month)
return "<pre>%s</pre>" % cal
GET__access__ = True
def GET(self, context):
name = self.name
return get_reference(name + '/;view_calendar')
class Year(Node):
def _get_object(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 MyRoot(Root):
def _get_object(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()
GET__access__ = True
def GET(self, context):
today = datetime.date.today()
path = today.strftime('%Y/%m/;view_calendar')
return get_reference(path)
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.
As the calendar example shows, with itools.web all nodes in the graph must be instances of the base class Node. And all of them will have two attributes:
node.parent
The parent node. For the root node it will be None.
node.name
The name of the node, this is to say the name it was used to reach the node from its parent. For the root node it will be the empty string.
Based on these two attributes, the Node class provides a rich API, here is an excerpt:
get_root()
Returns the root node.
get_abspath()
Returns the absolute path of this node, as a itools.uri.Reference instance.
get_pathto(node)
Returns the relative path from this node to the given node, as a itools.uri.Reference instance.
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.
_get_object(name)
Returns the node for the given name. If there is not any node with that name it must raise the LookupError exception.
Something new in this example is the value returned by the MyRoot.GET method is not a byte string, but a itools.uri.Reference instance. The values a method can return are:
a byte string
If everything is alright, a 200 OK response will be sent to the client, and the byte string will be its body.
a itools.uri.Reference instance
The client will be redirected to the given URI. That is to say, a response 302 Found will be sent to the client with the response header Location set to the given URI.
the value None
A response 204 No Content is sent to the client.
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.