Nevow Tutorial
Redefining locateChild()
Until now, each page has been responsible for accessing its direct children.
However, many web applications take the approach of encoding query parameters directly into URLs; this has the advantage of being easy to understand and easy to bookmark. An example of this might be:
http://examplebank.com/statements/58776376423/2006/11/byAmount/
Ignoring authentication and security issues, this fictitious URL might point to an on-line statement for account number 58776376423 covering the month of November 2006, with the transactions sorted by amount rather than transaction size.
However, the bank may have millions of accounts, and each one of these may have hundreds of months of history. Taking the approach that each unique path within a URL refers to a resource starts to become unfeasible when the possible number of URL combinations is huge.
Nevow handles this by allowing you to redefine a page's master lookup mechanism, locateChild(). This takes all path elements and returns a page and any path elements that it chose not to consume.
For example, consider the following example. Be aware that heavy reliance on the ctx variable is discouraged as it is deprecated; however, certain operations still require it.
###################################################################### # Run using 'twistd -noy file.tac', then point your browser to # http://localhost:8080 # A very simple Nevow site. ###################################################################### from twisted.application import service, internet from nevow import appserver from nevow import rend from nevow import loaders from nevow import tags as T from nevow import inevow class AccountPage ( rend.Page ): def render_title ( self, ctx, data ): request = inevow.IRequest ( ctx ) account_number = request.prepath [ 1 ] return "Account Summary for Account %s" % ( account_number, ) def render_body ( self, ctx, data ): request = inevow.IRequest ( ctx ) account_number = request.prepath [ 1 ] year = request.prepath [ 2 ] month = request.prepath [ 3 ] return "Account Balance at month end %s %s was %d" % \ ( year, month, int ( account_number ) + ( int ( year ) * int ( month ) ) ) docFactory = loaders.stan ( T.html [ T.head [ T.title [ render_title ] ], T.body [ render_body ] ] ) class ReportsPage ( rend.Page ): def locateChild ( self, ctx, segments ): return ( AccountPage(), () ) class MainPage ( rend.Page ): docFactory = loaders.stan ( T.html [ T.head ( title = 'Main Page' ), T.body [ T.h1 [ "This is the Main Page" ], T.p [ "Account Summaries:" ], T.p [ T.a ( href = '/reports/123456/2007/01' ) [ "Summary for account 123456" ] ], T.p [ T.a ( href = '/reports/234567/2006/02' ) [ "Summary for account 234567" ] ], T.p [ T.a ( href = '/reports/345678/2005/03' ) [ "Summary for account 345678" ] ] ], ] ) child_reports = ReportsPage() ###################################################################### # Nevow Boilerplate ###################################################################### application = service.Application ( "nevowdemo" ) port = 8080 res = MainPage() site = appserver.NevowSite ( res ) webService = internet.TCPServer ( port, site ) webService.setServiceParent ( application )
Note the following:
- The important change is the locateChild() method of ReportsPage;
- MainPage and ReportsPage are still accessed through conventional child lookups;
- Any path under /reports/ is consumed in its entirety by the locateChild() method of ReportsPage;
- For consistency in this simple example, the account balances are derived from the account number and the date; in a real application one hopes it would be retrieved from some sort of database;
- The titles and balances in AccountPage are calculated by inserting callables into the Stan; Stan is clever enough to know that it needs to call these and insert the results into the generated HTML.
