| 1 | Nevow Object Publishing |
|---|---|
| 2 | ======================= |
| 3 | |
| 4 | In Nevow Object Traversal, we learned about the |
| 5 | nevow.inevow.IResource.renderHTTP method, which is the most basic way to send |
| 6 | HTML to a browser when using Nevow. However, it is not very convenient (or |
| 7 | clean) to generate HTML tags by concatenating strings in Python code. In the |
| 8 | Nevow Deployment documentation, we saw that it was possible to render a Hello |
| 9 | World page using a nevow.rend.Page subclass and providing a "docFactory":: |
| 10 | |
| 11 | >>> from nevow import rend, loaders |
| 12 | >>> class HelloWorld(rend.Page): |
| 13 | ... docFactory = loaders.stan("Hello, world!") |
| 14 | ... |
| 15 | >>> HelloWorld().renderSynchronously() |
| 16 | 'Hello, world!' |
| 17 | |
| 18 | This example does nothing interesting, but the concept of a loader is important |
| 19 | in Nevow. The rend.Page.renderHTTP implementation always starts rendering HTML |
| 20 | by loading a template from the docFactory. |
| 21 | |
| 22 | * `The stan DOM`_ |
| 23 | * `Tag instances`_ |
| 24 | * `Functions in the DOM`_ |
| 25 | * `Accessing query parameters and form post data`_ |
| 26 | * `Generators in the DOM`_ |
| 27 | * `Methods in the DOM`_ |
| 28 | * `Data specials`_ |
| 29 | * `Render specials`_ |
| 30 | * `Pattern specials`_ |
| 31 | * `Slot specials`_ |
| 32 | * `Data directives`_ |
| 33 | * `Render directives`_ |
| 34 | * `Flatteners`_ |
| 35 | |
| 36 | The stan DOM |
| 37 | ------------ |
| 38 | |
| 39 | Nevow uses a DOM-based approach to rendering HTML. A tree of objects is first |
| 40 | constructed in memory by the template loader. This tree is then processed one |
| 41 | node at a time, applying functions which transform from various Python types to |
| 42 | HTML strings. |
| 43 | |
| 44 | Nevow uses a nonstandard DOM named "stan". Unlike the W3C DOM, stan is made up |
| 45 | of simple python lists, strings, and instances of the nevow.stan.Tag class. |
| 46 | During the rendering process, "Flattener" functions convert from rich types to |
| 47 | HTML strings. For example, we can load a template made up of some nested lists |
| 48 | and Python types, render it, and see what happens:: |
| 49 | |
| 50 | >>> class PythonTypes(rend.Page): |
| 51 | ... docFactory = loaders.stan(["Hello", 1, 1.5, True, ["Goodbye", 3]]) |
| 52 | ... |
| 53 | >>> PythonTypes().renderSynchronously() |
| 54 | 'Hello11.5TrueGoodbye3' |
| 55 | |
| 56 | Tag instances |
| 57 | ------------- |
| 58 | |
| 59 | So far, we have only rendered simple strings as output. However, the main |
| 60 | purpose of Nevow is HTML generation. In the stan DOM, HTML tags are represented |
| 61 | by instances of the nevow.stan.Tag class. Tag is a very simple class, whose |
| 62 | instances have an "attributes" dictionary and a "children" list. The Tag |
| 63 | flattener knows how to recursively flatten attributes and children of the tag. |
| 64 | To show you how Tags really work before you layer Nevow's convenience syntax on |
| 65 | top, try this horrible example:: |
| 66 | |
| 67 | >>> from nevow import stan |
| 68 | >>> h = stan.Tag('html') |
| 69 | >>> d = stan.Tag('div') |
| 70 | >>> d.attributes['style'] = 'border: 1px solid black' |
| 71 | >>> h.children.append(d) |
| 72 | >>> class Tags(rend.Page): |
| 73 | ... docFactory = loaders.stan(h) |
| 74 | ... |
| 75 | >>> Tags().renderSynchronously() |
| 76 | '<html><div style="border: 1px solid black"></div></html>' |
| 77 | |
| 78 | So, we see how it is possible to programatically generate HTML by constructing |
| 79 | and nesting stan Tag instances. However, it is far more convenient to use the |
| 80 | overloaded operators Tag provides to manipulate them. Tag implements a __call__ |
| 81 | method which takes any keyword arguments and values and updates the attributes |
| 82 | dictionary; it also implements a __getitem__ method which takes whatever is |
| 83 | between the square brackets and appends them to the children list. A simple |
| 84 | example should clarify things:: |
| 85 | |
| 86 | >>> class Tags2(rend.Page): |
| 87 | ... docFactory = loaders.stan(stan.Tag('html')[stan.Tag('div')(style="border: 1px solid black")]) |
| 88 | ... |
| 89 | >>> Tags2().renderSynchronously() |
| 90 | '<html><div style="border: 1px solid black"></div></html>' |
| 91 | |
| 92 | This isn't very easy to read, but luckily we can simplify the example even |
| 93 | further by using the nevow.tags module, which is full of "Tag prototypes" for |
| 94 | every tag type described by the XHTML 1.0 specification:: |
| 95 | |
| 96 | >>> class Tags3(rend.Page): |
| 97 | ... docFactory = loaders.stan(tags.html[tags.div(style="border: 1px solid black")]) |
| 98 | ... |
| 99 | >>> Tags3().renderSynchronously() |
| 100 | '<html><div style="border: 1px solid black"></div></html>' |
| 101 | |
| 102 | Using stan syntax is not the only way to construct template DOM for use by the |
| 103 | Nevow rendering process. Nevow also includes loaders.xmlfile which implements a |
| 104 | simple tag attribute language similar to the Zope Page Templates (ZPT) Tag |
| 105 | Attribute Language (TAL). However, experience with the stan DOM should give you |
| 106 | insight into how the Nevow rendering process really works. Rendering a template |
| 107 | into HTML in Nevow is really nothing more than iterating a tree of objects and |
| 108 | recursively applying "Flattener" functions to objects in this tree, until all |
| 109 | HTML has been generated. |
| 110 | |
| 111 | Functions in the DOM |
| 112 | -------------------- |
| 113 | |
| 114 | So far, all of our examples have generated static HTML pages, which is not |
| 115 | terribly interesting when discussing dynamic web applications. Nevow takes a |
| 116 | very simple approach to dynamic HTML generation. If you put a Python function |
| 117 | reference in the DOM, Nevow will call it when the page is rendered. The return |
| 118 | value of the function replaces the function itself in the DOM, and the results |
| 119 | are flattened further. This makes it easy to express looping and branching |
| 120 | structures in Nevow, because normal Python looping and branching constructs are |
| 121 | used to do the job:: |
| 122 | |
| 123 | >>> def repeat(ctx, data): |
| 124 | ... return [tags.div(style="color: %s" % (color, )) |
| 125 | ... for color in ['red', 'blue', 'green']] |
| 126 | ... |
| 127 | >>> class Repeat(rend.Page): |
| 128 | ... docFactory = loaders.stan(tags.html[repeat]) |
| 129 | ... |
| 130 | >>> Repeat().renderSynchronously() |
| 131 | '<html><div style="color: red"></div><div style="color: blue"></div><div style="color: green"></div></html>' |
| 132 | |
| 133 | However, in the example above, the repeat function isn't even necessary, because |
| 134 | we could have inlined the list comprehension right where we placed the function |
| 135 | reference in the DOM. Things only really become interesting when we begin |
| 136 | writing parameterized render functions which cause templates to render |
| 137 | differently depending on the input to the web application. |
| 138 | |
| 139 | The required signature of functions which we can place in the DOM is (ctx, |
| 140 | data). The "context" object is essentially opaque for now, and we will learn how |
| 141 | to extract useful information out of it later. The "data" object is anything we |
| 142 | want it to be, and can change during the rendering of the page. By default, the |
| 143 | data object is whatever we pass as the first argument to the Page constructor, |
| 144 | **or** the Page instance itself if nothing is passed. Armed with this knowledge, |
| 145 | we can create a Page which renders differently depending on the data we pass to |
| 146 | the Page constructor:: |
| 147 | |
| 148 | class Root(rend.Page): |
| 149 | docFactory = loaders.stan(tags.html[ |
| 150 | tags.h1["Welcome."], |
| 151 | tags.a(href="foo")["Foo"], |
| 152 | tags.a(href="bar")["Bar"], |
| 153 | tags.a(href="baz")["Baz"]]) |
| 154 | |
| 155 | def childFactory(self, ctx, name): |
| 156 | return Leaf(name) |
| 157 | |
| 158 | |
| 159 | def greet(ctx, name): |
| 160 | return "Hello. You are visiting the ", name, " page." |
| 161 | |
| 162 | class Leaf(rend.Page): |
| 163 | docFactory = loaders.stan(tags.html[greet]) |
| 164 | |
| 165 | Armed with this knowledge and the information in the Object Traversal |
| 166 | documentation, we now have enough information to create dynamic websites with |
| 167 | arbitrary URL hierarchies whose pages render dynamically depending on which URL |
| 168 | was used to access them. |
| 169 | |
| 170 | Accessing query parameters and form post data |
| 171 | --------------------------------------------- |
| 172 | |
| 173 | Before we move on to more advanced rendering techniques, let us first examine |
| 174 | how one could further customize the rendering of a Page based on the URL query |
| 175 | parameters and form post information provided to us by a browser. Recall that |
| 176 | URL parameters are expressed in the form:: |
| 177 | |
| 178 | http://example.com/foo/bar?baz=1&quux=2 |
| 179 | |
| 180 | And form post data can be generated by providing a form to a browser:: |
| 181 | |
| 182 | <form action="" method="POST"> |
| 183 | <input type="text" name="baz" /> |
| 184 | <input type="text" name="quux" /> |
| 185 | <input type="submit" /> |
| 186 | </form> |
| 187 | |
| 188 | Accessing this information is such a common procedure that Nevow provides a |
| 189 | convenience method on the context to do it. Let's examine a simple page whose |
| 190 | output can be influenced by the query parameters in the URL used to access it:: |
| 191 | |
| 192 | def showChoice(ctx, data): |
| 193 | choice = ctx.arg('choice') |
| 194 | if choice is None: |
| 195 | return '' |
| 196 | return "You chose ", choice, "." |
| 197 | |
| 198 | class Custom(rend.Page): |
| 199 | docFactory = loaders.stan(tags.html[ |
| 200 | tags.a(href="?choice=baz")["Baz"], |
| 201 | tags.a(href="?choice=quux")["Quux"], |
| 202 | tags.p[showChoice]]) |
| 203 | |
| 204 | The procedure is exactly the same for simple form post information:: |
| 205 | |
| 206 | def greet(ctx, data): |
| 207 | name = ctx.arg('name') |
| 208 | if name is None: |
| 209 | return '' |
| 210 | return "Greetings, ", name, "!" |
| 211 | |
| 212 | class Form(rend.Page): |
| 213 | docFactory = loaders.stan(tags.html[ |
| 214 | tags.form(action="", method="POST")[ |
| 215 | tags.input(name="name"), |
| 216 | tags.input(type="submit")], |
| 217 | greet]) |
| 218 | |
| 219 | Note that ctx.arg returns only the first argument with the given name. For |
| 220 | complex cases where multiple arguments and lists of argument values are |
| 221 | required, you can access the request argument dictionary directly using the |
| 222 | syntax:: |
| 223 | |
| 224 | def arguments(ctx, data): |
| 225 | args = inevow.IRequest(ctx).args |
| 226 | return "Request arguments are: ", str(args) |
| 227 | |
| 228 | Generators in the DOM |
| 229 | --------------------- |
| 230 | |
| 231 | One common operation when building dynamic pages is iterating a list of data and |
| 232 | emitting some HTML for each item. Python generators are well suited for |
| 233 | expressing this sort of logic, and code which is written as a python generator |
| 234 | can perform tests (if) and loops of various kinds (while, for) and emit a row of |
| 235 | html whenever it has enough data to do so. Nevow can handle generators in the |
| 236 | DOM just as gracefully as it can handle anything else:: |
| 237 | |
| 238 | >>> from nevow import rend, loaders, tags |
| 239 | >>> def generate(ctx, items): |
| 240 | ... for item in items: |
| 241 | ... yield tags.div[ item ] |
| 242 | ... |
| 243 | >>> class List(rend.Page): |
| 244 | ... docFactory = loaders.stan(tags.html[ generate ]) |
| 245 | ... |
| 246 | >>> List(['one', 'two', 'three']).renderSynchronously() |
| 247 | '<html><div>one</div><div>two</div><div>three</div></html>' |
| 248 | |
| 249 | As you can see, generating HTML inside of functions or generators can be very |
| 250 | convenient, and can lead to very rapid application development. However, it is |
| 251 | also what I would call a "template abstraction violation", and we will learn how |
| 252 | we can keep knowledge of HTML out of our python code when we learn about |
| 253 | patterns and slots. |
| 254 | |
| 255 | Methods in the DOM |
| 256 | ------------------ |
| 257 | |
| 258 | Up until now, we have been placing our template manipulation logic inside of |
| 259 | simple Python functions and generators. However, it is often appropriate to use |
| 260 | a method instead of a function. Nevow makes it just as easy to use a method to |
| 261 | render HTML:: |
| 262 | |
| 263 | class MethodRender(rend.Page): |
| 264 | def __init__(self, foo): |
| 265 | self.foo = foo |
| 266 | |
| 267 | def render_foo(self, ctx, data): |
| 268 | return self.foo |
| 269 | |
| 270 | docFactory = loaders.stan(tags.html[ render_foo ]) |
| 271 | |
| 272 | Using render methods makes it possible to parameterize your Page class with more |
| 273 | parameters. With render methods, you can also use the Page instance as a state |
| 274 | machine to keep track of the state of the render. While Nevow is designed to |
| 275 | allow you to render the same Page instance repeatedly, it can also be convenient |
| 276 | to know that a Page instance will only be used one time, and that the Page |
| 277 | instance can be used as a scratch pad to manage information about the render. |
| 278 | |
| 279 | Data specials |
| 280 | ------------- |
| 281 | |
| 282 | Previously we saw how passing a parameter to the default Page constructor makes |
| 283 | it available as the "data" parameter to all of our render methods. This "data" |
| 284 | parameter can change as the page render proceeds, and is a useful way to ensure |
| 285 | that render functions are isolated and only act upon the data which is available |
| 286 | to them. Render functions which do not pull information from sources other than |
| 287 | the "data" parameter are more easily reusable and can be composed into larger |
| 288 | parts more easily. |
| 289 | |
| 290 | Deciding which data gets passed as the data parameter is as simple as changing |
| 291 | the "Data special" for a Tag. See the Glossary under "Tag Specials" for more |
| 292 | information about specials. Assigning to the data special is as simple as |
| 293 | assigning to a tag attribute:: |
| 294 | |
| 295 | >>> def hello(ctx, name): |
| 296 | ... return "Hello, ", name |
| 297 | ... |
| 298 | >>> class DataSpecial(rend.Page): |
| 299 | ... docFactory = loaders.stan(tags.html[ |
| 300 | ... tags.div(data="foo")[ hello ], |
| 301 | ... tags.div(data="bar")[ hello ]]) |
| 302 | ... |
| 303 | >>> DataSpecial().renderSynchronously() |
| 304 | '<html><div>Hello, foo</div><div>Hello, bar</div></html>' |
| 305 | |
| 306 | Data specials may be assigned any python value. Data specials are only in scope |
| 307 | during the rendering of the tag they are assigned to, so if the "hello" renderer |
| 308 | were placed in the DOM inside the html node directly, "Hello, None" would be |
| 309 | output. |
| 310 | |
| 311 | Before data is passed to a render function, Nevow first checks to see if there |
| 312 | is an IGettable adapter for it. If there is, it calls IGettable.get(), and |
| 313 | passes the result of this as the data parameter instead. Nevow includes an |
| 314 | IGettable adapter for python functions, which means you can set a Tag data |
| 315 | special to a function reference and Nevow will call it to obtain the data when |
| 316 | the Tag is rendered. The signature for data methods is similar to that of render |
| 317 | methods, (ctx, data). For example:: |
| 318 | |
| 319 | def getName(ctx, data): |
| 320 | return ctx.arg('name') |
| 321 | |
| 322 | def greet(ctx, name): |
| 323 | return "Greetings, ", name |
| 324 | |
| 325 | class GreetName(rend.Page): |
| 326 | docFactory = loaders.stan(tags.html[ |
| 327 | tags.form(action="")[ |
| 328 | tags.input(name="name"), |
| 329 | tags.input(type="submit")], |
| 330 | tags.div(data=getName)[ greet ]]) |
| 331 | |
| 332 | Data specials exist mainly to allow you to construct and enforce a |
| 333 | Model-View-Controller style separation of the Model code from the View. Here we |
| 334 | see that the greet function is capable of rendering a greeting view for a name |
| 335 | model, and that the implementation of getName may change without the view code |
| 336 | changing. |
| 337 | |
| 338 | Render specials |
| 339 | --------------- |
| 340 | |
| 341 | Previously, we have seen how render functions can be placed directly in the DOM, |
| 342 | and the return value replaces the render function in the DOM. However, these |
| 343 | free functions and methods are devoid of any contextual information about the |
| 344 | template they are living in. The render special is a way to associate a render |
| 345 | function or method with a particular Tag instance, which the render function can |
| 346 | then examine to decide how to render:: |
| 347 | |
| 348 | >>> def alignment(ctx, data): |
| 349 | ... align = ctx.tag.attributes.get('align') |
| 350 | ... if align == 'right': |
| 351 | ... return ctx.tag["Aligned right"] |
| 352 | ... elif align == 'center': |
| 353 | ... return ctx.tag["Aligned center"] |
| 354 | ... else: |
| 355 | ... return ctx.tag["Aligned left"] |
| 356 | ... |
| 357 | >>> class AlignmentPage(rend.Page): |
| 358 | ... docFactory = loaders.stan(tags.html[ |
| 359 | ... tags.p(render=alignment), |
| 360 | ... tags.p(render=alignment, align="center"), |
| 361 | ... tags.p(render=alignment, align="right")]) |
| 362 | ... |
| 363 | >>> AlignmentPage().renderSynchronously() |
| 364 | '<html><p>Aligned left</p><p align="center">Aligned center</p><p align="right">Aligned right</p></html>' |
| 365 | |
| 366 | Note how the alignment renderer has access to the template node as "ctx.tag". It |
| 367 | can examine and change this node, and the return value of the render function |
| 368 | replaces the original node in the DOM. Note that here we are returning the |
| 369 | template node after changing it. We will see later how we can instead mutate the |
| 370 | context and use slots so that the knowledge the renderer requires about the |
| 371 | structure of the template is reduced even more. |
| 372 | |
| 373 | Pattern specials |
| 374 | ---------------- |
| 375 | |
| 376 | When writing render methods, it is easy to inline the construction of Tag |
| 377 | instances to generate HTML programatically. However, this creates a template |
| 378 | abstraction violation, where part of the HTML which will show up in the final |
| 379 | page output is hidden away inside of render methods instead of inside the |
| 380 | template. Pattern specials are designed to avoid this problem. A node which has |
| 381 | been tagged with a pattern special can then be located and copied by a render |
| 382 | method. The render method does not need to know anything about the structure or |
| 383 | location of the pattern, only it's name. |
| 384 | |
| 385 | We can rewrite our previous generator example so that the generator does not |
| 386 | have to know what type of tag the template designer would like repeated for each |
| 387 | item in the list:: |
| 388 | |
| 389 | >>> from nevow import rend, loaders, tags, inevow |
| 390 | >>> def generate(ctx, items): |
| 391 | ... pat = inevow.IQ(ctx).patternGenerator('item') |
| 392 | ... for item in items: |
| 393 | ... ctx.tag[ pat(data=item) ] |
| 394 | ... return ctx.tag |
| 395 | ... |
| 396 | >>> def string(ctx, item): |
| 397 | ... return ctx.tag[ str(item) ] |
| 398 | ... |
| 399 | >>> class List(rend.Page): |
| 400 | ... docFactory = loaders.stan(tags.html[ |
| 401 | ... tags.ul(render=generate)[ |
| 402 | ... tags.li(pattern="item", render=string)]]) |
| 403 | ... |
| 404 | >>> List([1, 2, 3]).renderSynchronously() |
| 405 | '<html><ol><li>1</li><li>2</li><li>3</li></ol></html>' |
| 406 | |
| 407 | Note that we have to mutate the tag in place and repeatedly copy the item |
| 408 | pattern, applying the item as the data special to the resulting Tag. It turns |
| 409 | out that this is such a common operation that nevow comes out of the box with |
| 410 | these two render functions:: |
| 411 | |
| 412 | >>> class List(rend.Page): |
| 413 | ... docFactory = loaders.stan(tags.html[ |
| 414 | ... tags.ul(render=rend.sequence)[ |
| 415 | ... tags.li(pattern="item", render=rend.data)]]) |
| 416 | ... |
| 417 | >>> List([1, 2, 3]).renderSynchronously() |
| 418 | '<html><ul><li>1</li><li>2</li><li>3</li></ul></html>' |
| 419 | |
| 420 | Slot specials |
| 421 | ------------- |
| 422 | |
| 423 | The problem with render methods is that they are only capable of making changes |
| 424 | to their direct children. Because of the architecture of Nevow, they should not |
| 425 | attempt to change grandchildren or parent nodes. It is possible to write one |
| 426 | render method for every node you wish to change, but there is a better way. A |
| 427 | node with a slot special can be "filled" with content by any renderer above the |
| 428 | slot. Creating a slot special is such a frequent task that there is a prototype |
| 429 | in nevow.tags which is usually used. |
| 430 | |
| 431 | Let us examine a renderer which fills a template with information about a |
| 432 | person: |
| 433 | |
| 434 | >>> from nevow import loaders, rend, tags |
| 435 | ... |
| 436 | >>> person = ('Donovan', 'Preston', 'Male', 'California') |
| 437 | ... |
| 438 | >>> def render_person(ctx, person): |
| 439 | ... firstName, lastName, sex, location = person |
| 440 | ... ctx.fillSlots('firstName', firstName) |
| 441 | ... ctx.fillSlots('lastName', lastName) |
| 442 | ... ctx.fillSlots('sex', sex) |
| 443 | ... ctx.fillSlots('location', location) |
| 444 | ... return ctx.tag |
| 445 | ... |
| 446 | >>> class PersonPage(rend.Page): |
| 447 | ... docFactory = loaders.stan(tags.html(render=render_person)[ |
| 448 | ... tags.table[ |
| 449 | ... tags.tr[ |
| 450 | ... tags.td[tags.slot('firstName')], |
| 451 | ... tags.td[tags.slot('lastName')], |
| 452 | ... tags.td[tags.slot('sex')], |
| 453 | ... tags.td[tags.slot('location')]]]]) |
| 454 | ... |
| 455 | >>> PersonPage(person).renderSynchronously() |
| 456 | '<html><table><tr><td>Donovan</td><td>Preston</td><td>Male</td><td>California</td></tr></table></html>' |
| 457 | |
| 458 | Using patterns in combination with slots can lead to very powerful template |
| 459 | abstraction. Nevow also includes another standard renderer called "mapping" |
| 460 | which takes any data which responds to the "items()" message and inserts the |
| 461 | items into appropriate slots:: |
| 462 | |
| 463 | >>> class DictPage(rend.Page): |
| 464 | ... docFactory = loaders.stan(tags.html(render=rend.mapping)[ |
| 465 | ... tags.span[ tags.slot('foo') ], tags.span[ tags.slot('bar') ]]) |
| 466 | ... |
| 467 | >>> DictPage(dict(foo=1, bar=2)).renderSynchronously() |
| 468 | '<html><span>1</span><span>2</span></html>' |
| 469 | |
| 470 | Data directives |
| 471 | --------------- |
| 472 | |
| 473 | So far, we have always placed data functions directly in the Data special |
| 474 | attribute of a Tag. Sometimes, it is preferable to look up a data method from |
| 475 | the Page class as the Page has being rendered. For example, a base class may |
| 476 | define a template and a subclass may provide the implementation of the data |
| 477 | method. We can accomplish this effect by using a data directive as a Tag's data |
| 478 | special:: |
| 479 | |
| 480 | class Base(rend.Page): |
| 481 | docFactory = loaders.stan(tags.html[ |
| 482 | tags.div(data=tags.directive('name'), render=rend.data)]) |
| 483 | |
| 484 | class Subclass(Base): |
| 485 | def data_name(self, ctx, data): |
| 486 | return "Your name" |
| 487 | |
| 488 | The data directive is resolved by searching for the IContainer implementation in |
| 489 | the context. rend.Page implements IContainer.get by performing an attribute |
| 490 | lookup on the Page with the prefix 'data_*'. You can provide your own IContainer |
| 491 | implementation if you wish, and also you should know that IContainer |
| 492 | implementations for list and dict are included in the nevow.accessors module. |
| 493 | |
| 494 | A common gotcha is that the closest IContainer is used to resolve data |
| 495 | directives. This means that if a list is being used as the data during the |
| 496 | rendering process, data directives below this will be resolved against the |
| 497 | IContainer implementation in nevow.accessors.ListAccessor. If you are expecting |
| 498 | a data directive to invoke a Page's data_* method but instead get a KeyError, |
| 499 | this is why. |
| 500 | |
| 501 | Render directives |
| 502 | ----------------- |
| 503 | |
| 504 | Render directives are almost exactly the same, except they are resolved using |
| 505 | the closest IRendererFactory implementation in the context. Render directives |
| 506 | can be used to allow subclasses to override certain render methods, and also can |
| 507 | be used to allow Fragments to locate their own prefixed render methods. |
| 508 | |
| 509 | Flatteners |
| 510 | ---------- |
| 511 | |
| 512 | TODO This section isn't done yet. |
| 513 | |
| 514 | Nevow's flatteners use a type/function registry to determine how to render |
| 515 | objects which Nevow encounters in the DOM during the rendering process. |
| 516 | "Explicit is better than implicit", so in most cases, explicitly applying render |
| 517 | methods to data will be better than registering a flattener, but in some cases |
| 518 | it can be useful:: |
| 519 | |
| 520 | class Person(object): |
| 521 | def __init__(self, firstName, lastName): |
| 522 | self.firstName = firstName |
| 523 | self.lastName = lastName |
| 524 | |
| 525 | def flattenPerson(person, ctx): |
| 526 | return flat.partialflatten( |
| 527 | ctx, |
| 528 | (person.firstName, " ", person.lastName)) |
| 529 | |
| 530 | from nevow import flat |
| 531 | flat.registerFlattener(flattenPerson, Person) |
| 532 | |
| 533 | def insertData(ctx, data): |
| 534 | return data |
| 535 | |
| 536 | class PersonPage(rend.Page): |
| 537 | docFactory = loaders.stan(tags.html[ insertData ]) |
| 538 |
相关推荐
nevow:Web 应用程序构建工具包
divmod 更新 Divmod Nevow 是一个用 Python 编写的 Web 应用程序构建工具包。 它旨在允许程序员根据需要在 Python 中表达尽可能多的视图逻辑,并包含一个名为 stan 的纯 Python XML 表达式语法来促进这一点。 然而,它还为设计者编辑的模板提供了丰富的支持,使用非常小的 XML 属性语言来提供双向模板操作能力。 Nevow 还包括 Divmod Athena,这是一个“双向网络”或“ ”实现,提供了服务器上的 Python 代码和客户端上的 JavaScript 代码之间的双向桥梁。 页面的模块化部分,在服务器 python 中称为“athena 片段”,在客户端 javascript 中称为“athena 小部件”,可以单独开发并放置在任何带有小型模板渲染器的 Nevow 渲染页面上。 Athena 在一个简单的远程方法调用接口背后抽象了
使用twisted+nevow框架简单实例
1. 首先,编写一个HTML文件,helloword.html Hello, world! Hello, world! 2. 编写一个Nevow控制程序文件,helloword.pyfrom nevow import loaders, rendclass HelloWorld(rend.Pa
制作Ubuntu22.04启动盘
现在越来越多程序代码都需要运行在linux环境下,运行这些代码的前提是安装一个稳定的linux操作系统。本文选择Ubuntu 22.04去制作系统启动盘。本文主要对ubuntu22.04的启动盘制作过程进行了详细的说明,希望能帮助到有需要的朋友。
小型Python Web框架 Nevow
Nevow ,python web, zhoubols, 周波
VxWorks环境搭建与学习:vxworks6.8和workbench3.2
VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统(RTOS),是嵌入式开发环境的关键组成部分。良好的持续能力、高性能的内核以及友好的用户开发环境,在嵌入式实时操作系统领域占据一席之地。它以其良好的可靠性和卓越的实时性被广泛地应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中,如卫星通讯、军事演习、弹道制导、飞机导航等。
ComfyUI+minimaxh3本地部署:漫剧分镜批量生成工作流全实操
视频生成模型正从云端API向本地化部署迁移,背后的技术逻辑是将扩散模型的文本编码、潜空间采样、VAE解码等阶段拆分为可编排的独立环节。ComfyUI作为节点化管线工具,恰好能以可视化连线方式管理这些阶段,让生成过程可控、可复用、可批量执行。minimaxh3模型支持本地推理,配合LoRA微调与参数优化,能够在保证角色一致性的前提下,从分镜脚本批量产出连续剧情片段,规避云端排队、成本高、素材不可控等工程问题。这套工作流广泛应用于漫剧、短剧和动态条漫的工业化生产,尤其适合需要高频迭代分镜草稿的内容团队。文本以零
Nevow XML Templates
1 Nevow XML Templates 2 =================== 3 4 Stan syntax is cool, but eventually you are going to want to integrate your 5 Python code with a template designed...
ORCA(动态避障)算法
A物体沿着向量v1和v2移动,刚好能和B擦肩而过,不会发生碰撞;若V1和V2的夹角再小一点的话就一定会发生碰撞。A的速度为V1,B的速度为V2,A对于B的相对速度(也就是我们把B看做静止,A的速度)就是V1-V2。,那么怎么判定A的速度向量是不是在碰撞区域内呢?这就需要用A相对于B的速度来判定。红线画出来的锥形区域就是A相对于B的。
Render_if: Conditional Parts in Nevow Templates
http://eagain.net/blog/2005/12/17/nevow_render_if.html def render_if(self, ctx, data): r=ctx.tag.allPatterns(str(bool(data))) return ctx.tag.clear()[r] <ul nevow:pattern="True"
ISP图像处理Verilog仿真框架搭建(附代码)
基于Verilog的图像处理仿真框架搭建
Unity 接入 Steam 平台
Unity 接入 steam sdk 的登录和应用内购买功能
一篇介绍Nevow的非常好的文章,推荐
Nevow: A Web Application Construction Kit Donovan Preston <dp@divmod.org > Summary Nevow is a web application construction kit, based on the ideas developed in the Twisted Woven pack...
【乐企文件生成工程】关于乐企文件生成工程的详细介绍
发票文件生成方式有两种思路:1、根据已有的OFD模板,动态替换ofd模板内容;之后将ofd转pdf(局限:单行问题不大)详情可以在此处了解2、根据html模板动态占位:该工程就是这种方式 ,目前效果良好,可看下图。拿去即用的源码!无任何保留,当然也并不是所有票种都支持(时间有限、精力有限、已经连着转了,要冒烟了…)
强化学习QLearning实践,小例子
最近在跟着莫烦大佬的系列教程学习强化学习,用了半天的时间终于弄清楚了Qlearning的来龙去脉,对莫烦大佬的代码做了些注释,读上去更容易理解些 import numpy as np import pandas as pd import time import tkinter as tk """ Black rectangles: hells [reward = -1]....
数据挖掘实战-基于KMeans算法对超市客户进行聚类分群
本实验基于KMeans算法对超市客户进行了聚类分群。通过对客户购物数据进行聚类,我们成功将客户分为不同的群体。每个群体代表了具有相似购物行为和偏好的客户群体。通过实验结果,我们发现了客户群体之间的明显差异和共性。不同群体的客户在购买频率、购物金额和购买种类等方面有所区别。这为超市提供了重要的市场细分信息,帮助超市经营者更好地了解不同客户群体的需求和喜好,制定有针对性的营销策略和商品推荐,提高客户满意度和忠诚度。此外,实验还揭示了超市客户的购物行为规律,为超市优化商品陈列和促销策略提供了指导。
汉书HE8,好用的汇编代码编写工具
汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大
体渲染简明教程【NeRF基础】
本课程前两章的目标是学习如何在均匀颜色的背景上渲染由单个光源照明的球体形状的体积。这将帮助我们首先直观地了解体积是什么,并介绍我们将用于渲染它们的光线行进算法。在这一章中,我们将只渲染一个具有均匀密度的普通体积。我们将忽略物体从体积外部或内部投射的阴影,以及如何以不同的密度渲染体积。这些将在接下来的章节中进行研究。我们不再提供大量关于什么是体积以及用于渲染它们的方程的详细背景知识,而是直接深入实现并从中获得对体积渲染的更正式的理解。
Statistics and Machine Learning Toolbox
Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具
Nerf体渲染和Neus提取网格原理
那么Marching cubes算法的基本思想就是根据每个小立方体各个顶点的体素值,计算这个立方体的三角形平面,进而用这个三角形平面表示我们3D物体的表面,但是如果一个立方体8个顶点的体素值都为1,也就是完全位于物体外,那么这个立方体是没有三角平面的,因为根本没有经过物体,同样如果8个体素值都为0,也就是完全位于物体内,那么这个立方体也是没有三角平面的,因此Marching cubes算法的目的是计算一些点位于物体表面外,其余点在物体表面内的立方体的三角平面。其中C代表的是颜色;tn和tf代表采样的范围;
大中型超市收银系统(可直接运行)
光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开
VB摄像头监控
该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制
【gym】env.render三种mode
研究了gym环境中的env.render函数的三种mode的使用效果
NeRF源码分析解读(四)
有了这两个量可以说我们已经模拟到了一条光线,下面我们继续分析代码,看看空间中的点是如何生成的,MLP又是怎样输出点的颜色和密度的。
<学习笔记>从零开始自学Python-之-web应用框架Django( 九)自定义标签和过滤器
Django 的模板语言自带了丰富的标签和过滤器,基本能满足常见的表现逻辑需求。 但有时候这些内建的标签和过滤器可能缺少我们需要的功能,这时候我们可以扩展模板引擎,使用 Python 自定义标签和过滤器,然后使用 {% load %} 标签加载,让自定义的标签和过滤器可在模板中使用。
关于django的render函数的参数问题
hello.html 文件代码如下: HelloWorld/templates/hello.html 文件代码: h1>{{ hello }}h1> HelloWorld/HelloWorld/view.py 文件代码: # -*- coding: utf-8 -*- #from django.http import HttpResponse f
Nevow拥有很好的新特性
引用:Nevow , a python based web construction toolkit , has a lot of nice features, that deserve attention:1 - LivePage ( Ajax ).2 - Canvas ( Flash to server ).3 - XUL , the python way.4 - Stan ( Python
[NeRF]代码+逻辑详细分析
0. 前言 鉴于最近两年(2020,2021),隐式渲染(implicit rendering)技术非常火爆(以NeRF和GRAFFE为代表),而由于这种隐式渲染需要一点点渲染的基础,而且相较于正常的CV任务不是很好理解。为了方便大家学习和理解,我这里将以ECCV2020的NeRF(神经辐射场 NeRF: Neural Radiance Field)[1]为例,对其进行代码级(基于pytorch3d[3]的实现)的详细剖析,希望对需要的朋友有所帮助。 1. 什么是NeRF 根据官方的项目[1], NeRF实
Django框架中定位URL的render、redirect和HttpResponse方法区别
Django中在views里有3种返回url的形式: HttpResponse:可以直接返回一个html页 render:用于页面渲染,需要传入request参数,除了html文件名,还可以传数据到页面上,源码里的介绍如下:实际还是调用HttpResponse def render(request, template_name, context=None, content_t...
python---redis缓存页面前戏之剖析render源码
1.初始代码: def get(self, *args, **kwargs): import time tm = time.time() self.render('home/index.html', time=tm) 2.代码追踪render: # 第一次执行get方法时,通过render方法已经生成了数据,具体存在地方,参见源码 ...
878



被折叠的 条评论
为什么被折叠?



