/usr/share/doc/python-nevow/howto/publishing.html is in python-nevow 0.10.0-4build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | <?xml version="1.0" encoding="utf-8"?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Nevow:
Object Publishing
</title>
<link href="stylesheet.css" rel="stylesheet" type="text/css"/>
</head>
<body bgcolor="white">
<h1 class="title">
Object Publishing
</h1>
<div class="toc"><ol><li><a href="#auto0">The stan DOM</a></li><li><a href="#auto1">Tag instances</a></li><li><a href="#auto2">Functions in the DOM</a></li><li><a href="#auto3">Accessing query parameters and form post data</a></li><li><a href="#auto4">Generators in the DOM</a></li><li><a href="#auto5">Methods in the DOM</a></li><li><a href="#auto6">Data specials</a></li><li><a href="#auto7">Render specials</a></li><li><a href="#auto8">Pattern specials</a></li><li><a href="#auto9">Slot specials</a></li><li><a href="#auto10">Data directives</a></li><li><a href="#auto11">Render directives</a></li><li><a href="#auto12">Flatteners</a></li></ol></div>
<div class="content">
<span/>
<p>
In <a href="traversal.html" shape="rect">Object Traversal</a>, we learned about the
<code class="API"><a href="nevow.inevow.IResource.renderHTTP" title="nevow.inevow.IResource.renderHTTP">nevow.inevow.IResource.renderHTTP</a></code> method, which
is the most basic way to send HTML to a browser when using
Nevow. However, it is not very convenient (or clean) to generate HTML
tags by concatenating strings in Python code. In the <a href="deployment.html" shape="rect">Deployment</a> documentation, we saw that it was
possible to render a <em>Hello World</em> page using a <code class="API"><a href="
" title="
">
nevow.rend.Page</a></code> subclass and providing a <code>docFactory</code>:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> from nevow import rend, loaders
>>> class HelloWorld(rend.Page):
... docFactory = loaders.stan("Hello, world!")
...
>>> HelloWorld().renderSynchronously()
'Hello, world!'</pre>
<p>
This example does nothing interesting, but the concept of a loader is
important in Nevow. The <code>rend.Page.renderHTTP</code> implementation
always starts rendering HTML by loading a template from the
<code>docFactory</code>.
</p>
<h2>The stan DOM<a name="auto0"/></h2>
<p>
Nevow uses a DOM-based approach to rendering HTML. A tree of objects is
first constructed in memory by the template loader. This tree is then
processed one node at a time, applying functions which transform from
various Python types to HTML strings.
</p>
<p>
Nevow uses a nonstandard DOM named "stan". Unlike the W3C DOM, stan is
made up of simple python lists, strings, and instances of the
nevow.stan.Tag class. During the rendering process, "Flattener"
functions convert from rich types to HTML strings. For example, we can
load a template made up of some nested lists and Python types, render it,
and see what happens:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> class PythonTypes(rend.Page):
... docFactory = loaders.stan(["Hello", 1, 1.5, True, ["Goodbye", 3]])
...
>>> PythonTypes().renderSynchronously()
'Hello11.5TrueGoodbye3'</pre>
<h2>Tag instances<a name="auto1"/></h2>
<p>
So far, we have only rendered simple strings as output. However, the main
purpose of Nevow is HTML generation. In the stan DOM, HTML tags are
represented by instances of the <code class="API"><a href="nevow.stan.Tag" title="nevow.stan.Tag">nevow.stan.Tag</a></code>
class. <code>Tag</code> is a very simple class, whose instances have an
<code>attributes</code> dictionary and a <code>children</code> list. The
<code>Tag</code> flattener knows how to recursively flatten attributes
and children of the tag. To show you how <code>Tag</code>s really work
before you layer Nevow's convenience syntax on top, try this horrible
example:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> from nevow import stan
>>> h = stan.Tag('html')
>>> d = stan.Tag('div')
>>> d.attributes['style'] = 'border: 1px solid black'
>>> h.children.append(d)
>>> class Tags(rend.Page):
... docFactory = loaders.stan(h)
...
>>> Tags().renderSynchronously()
'<html><div style="border: 1px solid black"></div></html>'</pre>
<p>
So, we see how it is possible to programatically generate HTML by
constructing and nesting stan <code>Tag</code> instances. However, it is
far more convenient to use the overloaded operators <code>Tag</code>
provides to manipulate them. <code>Tag</code> implements a
<code>__call__</code> method which takes any keyword arguments and values
and updates the attributes dictionary; it also implements a
<code>__getitem__</code> method which takes whatever is between the square
brackets and appends them to the children list. A simple example should
clarify things:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> class Tags2(rend.Page):
... docFactory = loaders.stan(stan.Tag('html')[stan.Tag('div')(style="border: 1px solid black")])
...
>>> Tags2().renderSynchronously()
'<html><div style="border: 1px solid black"></div></html>'</pre>
<p>
This isn't very easy to read, but luckily we can simplify the example
even further by using the nevow.tags module, which is full of "Tag
prototypes" for every tag type described by the XHTML 1.0 specification:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> class Tags3(rend.Page):
... docFactory = loaders.stan(tags.html[tags.div(style="border: 1px solid black")])
...
>>> Tags3().renderSynchronously()
'<html><div style="border: 1px solid black"></div></html>'</pre>
<p>
Using stan syntax is not the only way to construct template DOM for use
by the Nevow rendering process. Nevow also includes <code class="API"><a href="nevow.loaders.xmlfile" title="nevow.loaders.xmlfile">loaders.xmlfile</a></code> which implements a simple tag
attribute language similar to the Zope Page Templates (ZPT) Tag Attribute
Language (TAL). However, experience with the stan DOM should give you
insight into how the Nevow rendering process really works. Rendering a
template into HTML in Nevow is really nothing more than iterating a tree
of objects and recursively applying "Flattener" functions to objects in
this tree, until all HTML has been generated.
</p>
<h2>Functions in the DOM<a name="auto2"/></h2>
<p>
So far, all of our examples have generated static HTML pages, which is
not terribly interesting when discussing dynamic web applications. Nevow
takes a very simple approach to dynamic HTML generation. If you put a
Python function reference in the DOM, Nevow will call it when the page is
rendered. The return value of the function replaces the function itself
in the DOM, and the results are flattened further. This makes it easy to
express looping and branching structures in Nevow, because normal Python
looping and branching constructs are used to do the job:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> def repeat(ctx, data):
... return [tags.div(style="color: %s" % (color, ))
... for color in ['red', 'blue', 'green']]
...
>>> class Repeat(rend.Page):
... docFactory = loaders.stan(tags.html[repeat])
...
>>> Repeat().renderSynchronously()
'<html><div style="color: red"></div><div style="color: blue"></div><div style="color: green"></div></html>'</pre>
<p>
However, in the example above, the repeat function isn't even necessary,
because we could have inlined the list comprehension right where we
placed the function reference in the DOM. Things only really become
interesting when we begin writing parameterized render functions which
cause templates to render differently depending on the input to the web
application.
</p>
<p>
The required signature of functions which we can place in the DOM is
(ctx, data). The "context" object is essentially opaque for now, and we
will learn how to extract useful information out of it later. The "data"
object is anything we want it to be, and can change during the rendering
of the page. By default, the data object is whatever we pass as the first
argument to the Page constructor, <em>or</em> the Page instance itself if
nothing is passed. Armed with this knowledge, we can create a Page which
renders differently depending on the data we pass to the Page
constructor:
</p>
<pre class="python"><p class="py-linenumber"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</p><span class="py-src-keyword">class</span> <span class="py-src-identifier">Root</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">h1</span>[<span class="py-src-string">"Welcome."</span>],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">a</span>(<span class="py-src-variable">href</span>=<span class="py-src-string">"foo"</span>)[<span class="py-src-string">"Foo"</span>],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">a</span>(<span class="py-src-variable">href</span>=<span class="py-src-string">"bar"</span>)[<span class="py-src-string">"Bar"</span>],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">a</span>(<span class="py-src-variable">href</span>=<span class="py-src-string">"baz"</span>)[<span class="py-src-string">"Baz"</span>]])
<span class="py-src-keyword">def</span> <span class="py-src-identifier">childFactory</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">name</span>):
<span class="py-src-keyword">return</span> <span class="py-src-variable">Leaf</span>(<span class="py-src-variable">name</span>)
<span class="py-src-keyword">def</span> <span class="py-src-identifier">greet</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">name</span>):
<span class="py-src-keyword">return</span> <span class="py-src-string">"Hello. You are visiting the "</span>, <span class="py-src-variable">name</span>, <span class="py-src-string">" page."</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Leaf</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[<span class="py-src-variable">greet</span>])
</pre>
<p>
Armed with this knowledge and the information in the <a href="traversal.html" shape="rect">Object Traversal</a> documentation, we now have
enough information to create dynamic websites with arbitrary URL
hierarchies whose pages render dynamically depending on which URL was
used to access them.
</p>
<h2>Accessing query parameters and form post data<a name="auto3"/></h2>
<p>
Before we move on to more advanced rendering techniques, let us first
examine how one could further customize the rendering of a Page based on
the URL query parameters and form post information provided to us by a
browser. Recall that URL parameters are expressed in the form:
</p>
<pre xml:space="preserve">http://example.com/foo/bar?baz=1&quux=2</pre>
<p>
And form post data can be generated by providing a form to a browser:
</p>
<pre xml:space="preserve">
<form action="" method="POST">
<input type="text" name="baz" />
<input type="text" name="quux" />
<input type="submit" />
</form></pre>
<p>
Accessing this information is such a common procedure that Nevow provides
a convenience method on the context to do it. Let's examine a simple page
whose output can be influenced by the query parameters in the URL used to
access it:
</p>
<pre class="python"><p class="py-linenumber"> 1
2
3
4
5
6
7
8
9
10
11
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">showChoice</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-variable">choice</span> = <span class="py-src-variable">ctx</span>.<span class="py-src-variable">arg</span>(<span class="py-src-string">'choice'</span>)
<span class="py-src-keyword">if</span> <span class="py-src-variable">choice</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
<span class="py-src-keyword">return</span> <span class="py-src-string">''</span>
<span class="py-src-keyword">return</span> <span class="py-src-string">"You chose "</span>, <span class="py-src-variable">choice</span>, <span class="py-src-string">"."</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Custom</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">a</span>(<span class="py-src-variable">href</span>=<span class="py-src-string">"?choice=baz"</span>)[<span class="py-src-string">"Baz"</span>],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">a</span>(<span class="py-src-variable">href</span>=<span class="py-src-string">"?choice=quux"</span>)[<span class="py-src-string">"Quux"</span>],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">p</span>[<span class="py-src-variable">showChoice</span>]])
</pre>
<p>
The procedure is exactly the same for simple form post information:
</p>
<pre class="python"><p class="py-linenumber"> 1
2
3
4
5
6
7
8
9
10
11
12
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">greet</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-variable">name</span> = <span class="py-src-variable">ctx</span>.<span class="py-src-variable">arg</span>(<span class="py-src-string">'name'</span>)
<span class="py-src-keyword">if</span> <span class="py-src-variable">name</span> <span class="py-src-keyword">is</span> <span class="py-src-variable">None</span>:
<span class="py-src-keyword">return</span> <span class="py-src-string">''</span>
<span class="py-src-keyword">return</span> <span class="py-src-string">"Greetings, "</span>, <span class="py-src-variable">name</span>, <span class="py-src-string">"!"</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Form</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">form</span>(<span class="py-src-variable">action</span>=<span class="py-src-string">""</span>, <span class="py-src-variable">method</span>=<span class="py-src-string">"POST"</span>)[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">input</span>(<span class="py-src-variable">name</span>=<span class="py-src-string">"name"</span>),
<span class="py-src-variable">tags</span>.<span class="py-src-variable">input</span>(<span class="py-src-variable">type</span>=<span class="py-src-string">"submit"</span>)],
<span class="py-src-variable">greet</span>])
</pre>
<p>
Note that <code>ctx.arg</code> returns only the first argument with the
given name. For complex cases where multiple arguments and lists of
argument values are required, you can access the request argument
dictionary directly using the syntax:
</p>
<pre class="python"><p class="py-linenumber">1
2
3
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">arguments</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-variable">args</span> = <span class="py-src-variable">inevow</span>.<span class="py-src-variable">IRequest</span>(<span class="py-src-variable">ctx</span>).<span class="py-src-variable">args</span>
<span class="py-src-keyword">return</span> <span class="py-src-string">"Request arguments are: "</span>, <span class="py-src-variable">str</span>(<span class="py-src-variable">args</span>)
</pre>
<h2>Generators in the DOM<a name="auto4"/></h2>
<p>
One common operation when building dynamic pages is iterating a list of
data and emitting some HTML for each item. Python generators are well
suited for expressing this sort of logic, and code which is written as a
python generator can perform tests (<code>if</code>) and loops of various
kinds (<code>while</code>, <code>for</code>) and emit a row of html
whenever it has enough data to do so. Nevow can handle generators in the
DOM just as gracefully as it can handle anything else:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> from nevow import rend, loaders, tags
>>> def generate(ctx, items):
... for item in items:
... yield tags.div[ item ]
...
>>> class List(rend.Page):
... docFactory = loaders.stan(tags.html[ generate ])
...
>>> List(['one', 'two', 'three']).renderSynchronously()
'<html><div>one</div><div>two</div><div>three</div></html>'</pre>
<p>
As you can see, generating HTML inside of functions or generators can be
very convenient, and can lead to very rapid application
development. However, it is also what I would call a "template
abstraction violation", and we will learn how we can keep knowledge of
HTML out of our python code when we learn about patterns and slots.
</p>
<h2>Methods in the DOM<a name="auto5"/></h2>
<p>
Up until now, we have been placing our template manipulation logic inside
of simple Python functions and generators. However, it is often
appropriate to use a method instead of a function. Nevow makes it just as
easy to use a method to render HTML:
</p>
<pre class="python"><p class="py-linenumber">1
2
3
4
5
6
7
8
</p><span class="py-src-keyword">class</span> <span class="py-src-identifier">MethodRender</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">foo</span>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">foo</span> = <span class="py-src-variable">foo</span>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">render_foo</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-keyword">return</span> <span class="py-src-variable">self</span>.<span class="py-src-variable">foo</span>
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[ <span class="py-src-variable">render_foo</span> ])
</pre>
<p>
Using render methods makes it possible to parameterize your Page class
with more parameters. With render methods, you can also use the Page
instance as a state machine to keep track of the state of the
render. While Nevow is designed to allow you to render the same Page
instance repeatedly, it can also be convenient to know that a Page
instance will only be used one time, and that the Page instance can be
used as a scratch pad to manage information about the render.
</p>
<h2>Data specials<a name="auto6"/></h2>
<p>
Previously we saw how passing a parameter to the default Page constructor
makes it available as the "data" parameter to all of our render
methods. This "data" parameter can change as the page render proceeds,
and is a useful way to ensure that render functions are isolated and only
act upon the data which is available to them. Render functions which do
not pull information from sources other than the "data" parameter are
more easily reusable and can be composed into larger parts more easily.
</p>
<p>
Deciding which data gets passed as the data parameter is as simple as
changing the "Data special" for a Tag. See the <a href="glossary.html" shape="rect">Glossary</a> under "Tag Specials" for more
information about specials. Assigning to the data special is as simple as
assigning to a tag attribute:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> def hello(ctx, name):
... return "Hello, ", name
...
>>> class DataSpecial(rend.Page):
... docFactory = loaders.stan(tags.html[
... tags.div(data="foo")[ hello ],
... tags.div(data="bar")[ hello ]])
...
>>> DataSpecial().renderSynchronously()
'<html><div>Hello, foo</div><div>Hello, bar</div></html>'</pre>
<p>
Data specials may be assigned any python value. Data specials are only in
scope during the rendering of the tag they are assigned to, so if the
"hello" renderer were placed in the DOM inside the html node directly,
"Hello, None" would be output.
</p>
<p>
Before data is passed to a render function, Nevow first checks to see if
there is an <code class="API"><a href="IGettable" title="IGettable">IGettable</a></code> adapter for it. If there
is, it calls <code>IGettable.get()</code>, and passes the result of this
as the data parameter instead. Nevow includes an <code>IGettable</code>
adapter for python functions, which means you can set a Tag data special
to a function reference and Nevow will call it to obtain the data when
the Tag is rendered. The signature for data methods is similar to that of
render methods, (ctx, data). For example:
</p>
<pre class="python"><p class="py-linenumber"> 1
2
3
4
5
6
7
8
9
10
11
12
</p><span class="py-src-keyword">def</span> <span class="py-src-identifier">getName</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-keyword">return</span> <span class="py-src-variable">ctx</span>.<span class="py-src-variable">arg</span>(<span class="py-src-string">'name'</span>)
<span class="py-src-keyword">def</span> <span class="py-src-identifier">greet</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">name</span>):
<span class="py-src-keyword">return</span> <span class="py-src-string">"Greetings, "</span>, <span class="py-src-variable">name</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">GreetName</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">form</span>(<span class="py-src-variable">action</span>=<span class="py-src-string">""</span>)[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">input</span>(<span class="py-src-variable">name</span>=<span class="py-src-string">"name"</span>),
<span class="py-src-variable">tags</span>.<span class="py-src-variable">input</span>(<span class="py-src-variable">type</span>=<span class="py-src-string">"submit"</span>)],
<span class="py-src-variable">tags</span>.<span class="py-src-variable">div</span>(<span class="py-src-variable">data</span>=<span class="py-src-variable">getName</span>)[ <span class="py-src-variable">greet</span> ]])
</pre>
<p>
Data specials exist mainly to allow you to construct and enforce a
Model-View-Controller style separation of the Model code from the
View. Here we see that the greet function is capable of rendering a
greeting view for a name model, and that the implementation of getName
may change without the view code changing.
</p>
<h2>Render specials<a name="auto7"/></h2>
<p>
Previously, we have seen how render functions can be placed directly in
the DOM, and the return value replaces the render function in the
DOM. However, these free functions and methods are devoid of any
contextual information about the template they are living in. The
render special is a way to associate a render function or method with a
particular Tag instance, which the render function can then examine to
decide how to render:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> def alignment(ctx, data):
... align = ctx.tag.attributes.get('align')
... if align == 'right':
... return ctx.tag["Aligned right"]
... elif align == 'center':
... return ctx.tag["Aligned center"]
... else:
... return ctx.tag["Aligned left"]
...
>>> class AlignmentPage(rend.Page):
... docFactory = loaders.stan(tags.html[
... tags.p(render=alignment),
... tags.p(render=alignment, align="center"),
... tags.p(render=alignment, align="right")])
...
>>> AlignmentPage().renderSynchronously()
'<html><p>Aligned left</p><p align="center">Aligned center</p><p align="right">Aligned right</p></html>'</pre>
<p>
Note how the alignment renderer has access to the template node as
<code>ctx.tag</code>. It can examine and change this node, and the return value of
the render function replaces the original node in the DOM. Note that
here we are returning the template node after changing it. We will see
later how we can instead mutate the context and use slots so that the
knowledge the renderer requires about the structure of the template is
reduced even more.
</p>
<h2>Pattern specials<a name="auto8"/></h2>
<p>
When writing render methods, it is easy to inline the construction of
Tag instances to generate HTML programatically. However, this creates a
template abstraction violation, where part of the HTML which will show
up in the final page output is hidden away inside of render methods
instead of inside the template. Pattern specials are designed to avoid
this problem. A node which has been tagged with a pattern special can
then be located and copied by a render method. The render method does
not need to know anything about the structure or location of the
pattern, only it's name.
</p>
<p>
We can rewrite our previous generator example so that the generator
does not have to know what type of tag the template designer would like
repeated for each item in the list:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> from nevow import rend, loaders, tags, inevow
>>> def generate(ctx, items):
... pat = inevow.IQ(ctx).patternGenerator('item')
... for item in items:
... ctx.tag[ pat(data=item) ]
... return ctx.tag
...
>>> def string(ctx, item):
... return ctx.tag[ str(item) ]
...
>>> class List(rend.Page):
... docFactory = loaders.stan(tags.html[
... tags.ul(render=generate)[
... tags.li(pattern="item", render=string)]])
...
>>> List([1, 2, 3]).renderSynchronously()
'<html><ol><li>1</li><li>2</li><li>3</li></ol></html>'</pre>
<p>
Note that we have to mutate the tag in place and repeatedly copy the
item pattern, applying the item as the data special to the resulting
Tag. It turns out that this is such a common operation that nevow comes
out of the box with these two render functions:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> class List(rend.Page):
... docFactory = loaders.stan(tags.html[
... tags.ul(render=rend.sequence)[
... tags.li(pattern="item", render=rend.data)]])
...
>>> List([1, 2, 3]).renderSynchronously()
'<html><ul><li>1</li><li>2</li><li>3</li></ul></html>'</pre>
<h2>Slot specials<a name="auto9"/></h2>
<p>
The problem with render methods is that they are only capable of making
changes to their direct children. Because of the architecture of Nevow,
they should not attempt to change grandchildren or parent nodes. It is
possible to write one render method for every node you wish to change,
but there is a better way. A node with a slot special can be "filled"
with content by any renderer above the slot. Creating a slot special is
such a frequent task that there is a prototype in <code>nevow.tags</code>
which is usually used.
</p>
<p>
Let us examine a renderer which fills a template with information about
a person:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> from nevow import loaders, rend, tags
...
>>> person = ('Donovan', 'Preston', 'Male', 'California')
...
>>> def render_person(ctx, person):
... firstName, lastName, sex, location = person
... ctx.fillSlots('firstName', firstName)
... ctx.fillSlots('lastName', lastName)
... ctx.fillSlots('sex', sex)
... ctx.fillSlots('location', location)
... return ctx.tag
...
>>> class PersonPage(rend.Page):
... docFactory = loaders.stan(tags.html(render=render_person)[
... tags.table[
... tags.tr[
... tags.td[tags.slot('firstName')],
... tags.td[tags.slot('lastName')],
... tags.td[tags.slot('sex')],
... tags.td[tags.slot('location')]]]])
...
>>> PersonPage(person).renderSynchronously()
'<html><table><tr><td>Donovan</td><td>Preston</td><td>Male</td><td>California</td></tr></table></html>'</pre>
<p>
Using patterns in combination with slots can lead to very powerful
template abstraction. Nevow also includes another standard renderer
called "mapping" which takes any data which responds to the "items()"
message and inserts the items into appropriate slots:
</p>
<pre class="python-interpreter" xml:space="preserve">
>>> class DictPage(rend.Page):
... docFactory = loaders.stan(tags.html(render=rend.mapping)[
... tags.span[ tags.slot('foo') ], tags.span[ tags.slot('bar') ]])
...
>>> DictPage(dict(foo=1, bar=2)).renderSynchronously()
'<html><span>1</span><span>2</span></html>'</pre>
<h2>Data directives<a name="auto10"/></h2>
<p>
So far, we have always placed data functions directly in the Data
special attribute of a Tag. Sometimes, it is preferable to look up a
data method from the Page class as the Page has being rendered. For
example, a base class may define a template and a subclass may provide
the implementation of the data method. We can accomplish this effect by
using a data directive as a Tag's data special:
</p>
<pre class="python"><p class="py-linenumber">1
2
3
4
5
6
7
</p><span class="py-src-keyword">class</span> <span class="py-src-identifier">Base</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[
<span class="py-src-variable">tags</span>.<span class="py-src-variable">div</span>(<span class="py-src-variable">data</span>=<span class="py-src-variable">tags</span>.<span class="py-src-variable">directive</span>(<span class="py-src-string">'name'</span>), <span class="py-src-variable">render</span>=<span class="py-src-variable">rend</span>.<span class="py-src-variable">data</span>)])
<span class="py-src-keyword">class</span> <span class="py-src-identifier">Subclass</span>(<span class="py-src-parameter">Base</span>):
<span class="py-src-keyword">def</span> <span class="py-src-identifier">data_name</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-keyword">return</span> <span class="py-src-string">"Your name"</span>
</pre>
<p>
The data directive is resolved by searching for the
<code>IContainer</code> implementation in the context.
<code>rend.Page</code> implements <code>IContainer.get</code> by
performing an attribute lookup on the Page with the prefix 'data_*'. You
can provide your own <code>IContainer</code> implementation if you wish,
and also you should know that <code>IContainer</code> implementations for
list and dict are included in the <code class="API"><a href="nevow.accessors" title="nevow.accessors">nevow.accessors</a></code>
module.
</p>
<p>
A common gotcha is that the closest <code>IContainer</code> is used to
resolve data directives. This means that if a list is being used as the
data during the rendering process, data directives below this will be
resolved against the <code>IContainer</code> implementation in
<code>nevow.accessors.ListAccessor</code>. If you are expecting a data
directive to invoke a Page's data_* method but instead get a
<code>KeyError</code>, this is why.
</p>
<h2>Render directives<a name="auto11"/></h2>
<p>
Render directives are almost exactly the same, except they are resolved
using the closest <code>IRendererFactory</code> implementation in the
context. Render directives can be used to allow subclasses to override
certain render methods, and also can be used to allow Fragments to
locate their own prefixed render methods.
</p>
<h2>Flatteners<a name="auto12"/></h2>
<p>
TODO This section isn't done yet.
</p>
<p>
Nevow's flatteners use a type/function registry to determine how to
render objects which Nevow encounters in the DOM during the rendering
process. "Explicit is better than implicit", so in most cases,
explicitly applying render methods to data will be better than
registering a flattener, but in some cases it can be useful:
</p>
<pre class="python"><p class="py-linenumber"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</p><span class="py-src-keyword">class</span> <span class="py-src-identifier">Person</span>(<span class="py-src-parameter">object</span>):
<span class="py-src-keyword">def</span> <span class="py-src-identifier">__init__</span>(<span class="py-src-parameter">self</span>, <span class="py-src-parameter">firstName</span>, <span class="py-src-parameter">lastName</span>):
<span class="py-src-variable">self</span>.<span class="py-src-variable">firstName</span> = <span class="py-src-variable">firstName</span>
<span class="py-src-variable">self</span>.<span class="py-src-variable">lastName</span> = <span class="py-src-variable">lastName</span>
<span class="py-src-keyword">def</span> <span class="py-src-identifier">flattenPerson</span>(<span class="py-src-parameter">person</span>, <span class="py-src-parameter">ctx</span>):
<span class="py-src-keyword">return</span> <span class="py-src-variable">flat</span>.<span class="py-src-variable">partialflatten</span>(<span class="py-src-variable">ctx</span>, (<span class="py-src-variable">person</span>.<span class="py-src-variable">firstName</span>, <span class="py-src-string">" "</span>, <span class="py-src-variable">person</span>.<span class="py-src-variable">lastName</span>))
<span class="py-src-keyword">from</span> <span class="py-src-variable">nevow</span> <span class="py-src-keyword">import</span> <span class="py-src-variable">flat</span>
<span class="py-src-variable">flat</span>.<span class="py-src-variable">registerFlattener</span>(<span class="py-src-variable">flattenPerson</span>, <span class="py-src-variable">Person</span>)
<span class="py-src-keyword">def</span> <span class="py-src-identifier">insertData</span>(<span class="py-src-parameter">ctx</span>, <span class="py-src-parameter">data</span>):
<span class="py-src-keyword">return</span> <span class="py-src-variable">data</span>
<span class="py-src-keyword">class</span> <span class="py-src-identifier">PersonPage</span>(<span class="py-src-parameter">rend</span>.<span class="py-src-parameter">Page</span>):
<span class="py-src-variable">docFactory</span> = <span class="py-src-variable">loaders</span>.<span class="py-src-variable">stan</span>(<span class="py-src-variable">tags</span>.<span class="py-src-variable">html</span>[<span class="py-src-variable">insertData</span>])
</pre>
</div>
<p><a href="index.html">Index</a></p>
<span class="version">Version: </span>
</body>
</html>
|