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 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.
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.
In a word, by traversal we basically understand the process of:
Picking the resource in the tree identified by the given path.
Picking a method of this resource, 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, 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.
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:
resource.parent
The parent resource. For the root resource it will be None.
resource.name
The name of the resource, this is to say the name it was used to reach the resource from its parent. For the root resource it will be the empty string.
Based on these two attributes, the Resource class provides a rich API, here is an excerpt:
get_root()
Returns the root resource.
get_abspath()
Returns the absolute path of this resource, as a itools.uri.Reference instance.
get_pathto(resource)
Returns the relative path from this resource to the given resource, 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_resource(name)
Returns the resource for the given name. If there is not any resource with that name it must raise the LookupError exception.
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:
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.