[Twisted-web] nested table sequences example

Andrea Arcangeli andrea at cpushare.com
Mon May 16 01:52:25 MDT 2005


Hello,

Before the weekend I asked some hint to dialtone on how to eliminate an
huge cut-and-pasting in the template book of CPUShare.

He quickly told me to use the IQ(ctx).patternGenerator to fill in the
data for the tables. That was the information I needed to get on the
right track and after some fighting (worst part to figure out was how to
write the right currency in the header) I got it working. Resulting html
is exactly the same, but rendering time is down from 600msec to 250msec
and more important there's no code duplication in the rendering and
template anymore.

I'm willing to share this code for nevow documentation purposes just in
case anybody else has similar needs, since I didn't see examples
covering nested tables usages like this yet.

This below is the template. Before the weekend it was 532 lines (I was
using only many n:data="xx" and n:render="sequence", with many different
perspective calls). It was a nightmare to maintain, every time I wanted
to do a cosmetical change, I had to cut-and-paste it 5 times all over
the >500 lines file. Now after this cleanup the same template is down to
103 lines:

<n:invisible xmlns:n="http://nevow.com/ns/nevow/0.1">

<table class="book_outside" n:data="book" n:render="book_composite">

  <table class="account" n:pattern="book_sequence_bid" n:render="book_sequence">
    <n:invisible n:pattern="header" n:render="book_header">
      <caption class="book">
	<n:slot name="currency" /> Bid
      </caption>
      <tr class="account">
	<th class="account">Price</th>
	<th class="account">Time</th>
	<th class="account">Size</th>
	<th class="account">MFLOPS</th>
	<th class="account">RTT</th>
	<th class="account">RAM</th>
	<th class="account">CPUCoins</th>
      </tr>
    </n:invisible>

    <tr class="account" n:pattern="item" n:render="book_bid">
      <td class="account_right"><pre class="book_right"><n:slot name="price" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="time" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="size" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="mflops" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="rtt" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="ram_mbytes" /></pre></td>
      <td class="account_right"><pre class="book_center"><n:slot name="cpucoins" /></pre></td>
    </tr>

    <tr class="account" n:pattern="item" n:render="book_bid">
      <td class="account_right_even"><pre class="book_right"><n:slot name="price" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="time" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="size" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="mflops" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="rtt" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="ram_mbytes" /></pre></td>
      <td class="account_right_even"><pre class="book_center"><n:slot name="cpucoins" /></pre></td>
    </tr>

    <tr class="account" n:pattern="empty">
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
    </tr>

  </table>

  <table class="account" n:pattern="book_sequence_ask" n:render="book_sequence">
    <n:invisible n:pattern="header" n:render="book_header">
      <caption class="book">
	<n:slot name="currency" /> Ask
      </caption>
      <tr class="account">
	<th class="account">Price</th>
	<th class="account">Time</th>
	<th class="account">MFLOPS</th>
	<th class="account">RTT</th>
	<th class="account">RAM</th>
	<th class="account">CPUCoins</th>
	<th class="account">Arch</th>
      </tr>
    </n:invisible>

    <tr class="account" n:pattern="item" n:render="book_ask">
      <td class="account_right"><pre class="book_right"><n:slot name="price" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="time" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="mflops" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="rtt" /></pre></td>
      <td class="account_right"><pre class="book_right"><n:slot name="ram_mbytes" /></pre></td>
      <td class="account_right"><pre class="book_center"><n:slot name="cpucoins" /></pre></td>
      <td class="account_right"><pre class="book_center"><n:slot name="arch" /></pre></td>
    </tr>

    <tr class="account" n:pattern="item" n:render="book_ask">
      <td class="account_right_even"><pre class="book_right"><n:slot name="price" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="time" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="mflops" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="rtt" /></pre></td>
      <td class="account_right_even"><pre class="book_right"><n:slot name="ram_mbytes" /></pre></td>
      <td class="account_right_even"><pre class="book_center"><n:slot name="cpucoins" /></pre></td>
      <td class="account_right_even"><pre class="book_center"><n:slot name="arch" /></pre></td>
    </tr>

    <tr class="account" n:pattern="empty">
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
      <td class="account_dashed">-</td>
    </tr>

  </table>

</table>

</n:invisible>

And this is the rendering code:

BOOK_SIZE = 10
BOOK_TIME_STRFTIME = '%y%m%d %H:%M:%S'

class book_content_class(rend.Fragment):
	docFactory = loaders.xmlfile('book.xml', templateDir = XMLDIR, ignoreDocType = True)

	def data_book(self, ctx, data):
		try:
			return IPB(ctx).call('get_books', BOOK_SIZE)
		except AttributeError:
			return []
	def render_book_composite(self, ctx, data):
		ptrn_bid = inevow.IQ(ctx).patternGenerator('book_sequence_bid')
		ptrn_ask = inevow.IQ(ctx).patternGenerator('book_sequence_ask')
		return ctx.tag[[ tags.tr(_class="book_outside")
				 [tags.td(_class="book_outside")[ptrn_bid(data=(x[0], x[1]))],
				  tags.td(_class="book_outside")[ptrn_ask(data=(x[0], x[2]))]]
				 for x in data ]]
	def render_book_sequence(self, ctx, data): 
		'This is the same as "sequence" but uses data[1] instead of data'
		tag = ctx.tag
		headers = tag.allPatterns('header')
		pattern = tag.patternGenerator('item')
		divider = tag.patternGenerator('divider', default=tags.invisible)
		content = [(pattern(data=element), divider(data=element)) for element in data[1]]
		if not content:
			content = tag.allPatterns('empty')
		else:
			## No divider after the last thing.
			content[-1] = content[-1][0]
		footers = tag.allPatterns('footer')

		return tag.clear()[ headers, content, footers ]
	def render_book_header(self, ctx, data):
		ctx.fillSlots('currency', data[0])
		return ctx.tag
	def render_book_ask(self, ctx, data):
		ctx.fillSlots('price', data[0])
		ctx.fillSlots('time', time.strftime(BOOK_TIME_STRFTIME, time.localtime(data[1])))
		ctx.fillSlots('mflops', data[2])
		ctx.fillSlots('rtt', data[3])
		ctx.fillSlots('ram_mbytes', data[4])
		ctx.fillSlots('cpucoins', data[5])
		ctx.fillSlots('arch', data[6])
		return ctx.tag
	def render_book_bid(self, ctx, data):
		ctx.fillSlots('price', data[0])
		ctx.fillSlots('time', time.strftime(BOOK_TIME_STRFTIME, time.localtime(data[1])))
		ctx.fillSlots('size', data[2])
		ctx.fillSlots('mflops', data[3])
		ctx.fillSlots('rtt', data[4])
		ctx.fillSlots('ram_mbytes', data[5])
		ctx.fillSlots('cpucoins', data[6])
		return ctx.tag

The list returned by the get_books perspective client, is created by the
pb server like this: [[currency1, [bidsequence], [asksequnce]], [currency2,
[bidsequence], [asksequence]],...].

You can see that I had to create my own render_book_sequence that works
exactly like the default render_sequence, except that uses "data[1]"
instead of "data". I didn't find any other way than to create my own
render_book_sequence, to be able to fill in the currency information in
render_book_header.

Hope this helps!



More information about the Twisted-web mailing list