Recipe14.14.Rendering Arbitrary Objects with Nevow


Recipe 14.14. Rendering Arbitrary Objects with Nevow

Credit: Valentino Volonghi, Matt Goodall

Problem

You're writing a web application that uses the Twisted networking framework and the Nevow subsystem for web rendering. You need to be able to render some arbitrary Python objects to a web page.

Solution

Interfaces and adapters are the Twisted and Nevow approach to this task. Here is a toy example web server script to show how they work:

from twisted.application import internet, service from nevow import appserver, compy, inevow, loaders, rend from nevow import tags as T # Define some simple classes to be the example's "application data" class Person(object):     def _ _init_ _(self, firstName, lastName, nickname):         self.firstName = firstName         self.lastName = lastName         self.nickname = nickname class Bookmark(object):     def _ _init_ _(self, name, url):         self.name = name         self.url = url # Adapter subclasses are the right way to join application data to the web: class PersonView(compy.Adapter):     """ Render a full view of a Person. """     _ _implements_ _ = inevow.IRenderer     attrs = 'firstName', 'lastName', 'nickname'     def rend(self, data):         return T.div(_) [             T.p['Person'],             T.dl[ [(T.dt[attr], T.dd[getattr(self.original, attr)])                     for attr in self.attrs]                 ]             ] class BookmarkView(compy.Adapter):     """ Render a full view of a Bookmark. """     _ _implements_ _ = inevow.IRenderer     attrs = 'name', 'url'     def rend(self, data):         return T.div(_) [             T.p['Bookmark'],             T.dl[ [(T.dt[attr], T.dd[getattr(self.original, attr)])                     for attr in self.attrs]                 ]             ] # register the rendering adapters (could be done from a config textfile) compy.registerAdapter(PersonView, Person, inevow.IRenderer) compy.registerAdapter(BookmarkView, Bookmark, inevow.IRenderer) # some example data instances for the 'application' objs = [     Person('Valentino', 'Volonghi', 'dialtone'),     Person('Matt', 'Goodall', 'mg'),     Bookmark('Nevow', 'http://www.nevow.com'),     Person('Alex', 'Martelli', 'aleax'),     Bookmark('Alex', 'http://www.aleax.it/'),     Bookmark('Twisted', 'http://twistedmatrix.com/'),     Bookmark('Python', 'http://www.python.org'),     ] # a simple Page that renders a list of objects class Page(rend.Page):     def render_item(self, ctx, data):         return inevow.IRenderer(data)     docFactory = loaders.stan(         T.html[             T.body[                 T.ul(data=objs, render=rend.sequence)[                     T.li(pattern='item')[render_item],                     ],                 ],             ]         ) # start this very-special-purpose tiny toy webserver: application = service.Application('irenderer') httpd = internet.TCPServer(8000, appserver.NevowSite(Page( ))) httpd.setServiceParent(application)

Discussion

This recipe's purpose is to provide an example of how to get Nevow to render instances of application classes directly to a web page. To supply this example, the recipe shows two classes, Person and Bookmark, whose instances contain information which, one can suppose, is coming from a database, or from a file, or from some other site on the web, wherever.

A key point is that the application classes do not get altered in any way to allow their instances to be rendered onto web pages: rather, adaptation is used to allow instances of such classes to be rendered through separate renderer-adapter classes.

We need two different adapters, one each for Person and Bookmark. We code the two adapters as classes PersonView and BookmarkView, each inheriting from compy.Adapter and overriding the rend method.

compy.Adapter is an abstract superclass intended just for this purpose: it accepts as its constructor argument an object that must be adapted to another interface, and holds that object as self.original for its subclasses' benefit. Each subclass asserts that it implements inevow.IRenderer by listing that interface in its class-level _ _implements_ _ attribute.

inevow.IRenderer is an interface that supplies a rend method. The Nevow rendering pipeline knows about IRenderer and calls the rend method of the interface to serialize objects to HTML. Objects that implement the interface (on their own behalf or as adapters of other objects) can directly become part of the rendering pipeline.

The two key statements of this recipe are the two calls to the registerAdapter function of Nevow's module compy:

compy.registerAdapter(PersonView, Person, inevow.IRenderer) compy.registerAdapter(BookmarkView, Bookmark, inevow.IRenderer)

These calls tell Nevow that PersonView is the class to use to adapt any instance of Person to interface IRenderer, and similarly for BookmarkView and Bookmark. So, when the IRenderer interface is called with an instance p of Person as its argument, it automatically returns an adapter that is an instance of PersonView with p as its self.original (and, again, similarly for Bookmark).

Note how accurately this approach distributes appropriate knowledge to the various parts of the software and minimizes coupling among them while strengthening cohesion within each. Nevow itself has no built-in knowledge of any application class nor of any specific adapter: nor does it need any such knowledge. Nevow just specifies the IRenderer interface it needs for rendering and the registerAdapter function used to inform the framework about adaptation connections. Application-level classes neither have nor need any knowledge of the framework at all. Each adapter class knows about the application level class it's adapting, the interface it's implementing, and utilities such as the Adapter base class that the framework supplies (just to factor out a little repetitive coding that would be needed otherwise), and the tags mechanism. (The tags mechanism eases dynamic generation of HTML output. However, you could code adapters to return strings with HTML markup directly, if that suited the needs of your specific application better than the tags mechanism does.)

Finally, the recipe includes an example Page class which ties everything togetheragain, for convenience, using tags to generate the output. Page uses (explicitly) the rend.sequence renderer provided by Nevow to loop over a sequence and render each item, and (implicitly) the various adapters, by "casting" each item to the IRenderer interface. The recipe ends with three lines to build Twisted application and service objects and to put them together, so that running this recipe's script with Twisted's twistd general-purpose daemon provides a small demonstration one-page web site running on the local host at port 8000.

A more complete (and complicated) version of this recipe can be found as part of the Nevow 0.3 distribution, downloadable from http://www.nevow.com, as examples/irenderer.tac.

See Also

Nevow is at http://www.nevow.com; Twisted is at http://twistedmatrix.com/.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net