diff options
| author | Paul Everitt <paul@agendaless.com> | 2008-07-25 15:24:07 +0000 |
|---|---|---|
| committer | Paul Everitt <paul@agendaless.com> | 2008-07-25 15:24:07 +0000 |
| commit | 992ef769cbbcb7a47b53d299e551b70d80e993b9 (patch) | |
| tree | 26cc57f79b8dfeec6c7224d99320d0468677f74a | |
| parent | 3bb4cdcbd5c0a5ddb994d1f12f1f38066fc49cba (diff) | |
| download | pyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.tar.gz pyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.tar.bz2 pyramid-992ef769cbbcb7a47b53d299e551b70d80e993b9.zip | |
Step 4 written up. I also made the small changes to the models.py that Chris had in Step 03.
| -rw-r--r-- | docs/tutorials/lxmlgraph/index.rst | 1 | ||||
| -rw-r--r-- | docs/tutorials/lxmlgraph/step04.rst | 159 | ||||
| -rw-r--r-- | docs/tutorials/lxmlgraph/step04/myapp/models.py | 6 |
3 files changed, 162 insertions, 4 deletions
diff --git a/docs/tutorials/lxmlgraph/index.rst b/docs/tutorials/lxmlgraph/index.rst index 385968d42..a4d0016cc 100644 --- a/docs/tutorials/lxmlgraph/index.rst +++ b/docs/tutorials/lxmlgraph/index.rst @@ -14,3 +14,4 @@ describes publishing an XML document as a hierarchical website. step01 step02 step03 + step04 diff --git a/docs/tutorials/lxmlgraph/step04.rst b/docs/tutorials/lxmlgraph/step04.rst index 966406319..35e0b9aa9 100644 --- a/docs/tutorials/lxmlgraph/step04.rst +++ b/docs/tutorials/lxmlgraph/step04.rst @@ -76,8 +76,8 @@ That's the big picture. Each of these changes will be explained in detail below. -File ``samplemodel.xml`` -=================================== +``samplemodel.xml`` +===================== The XML document with the information for our website has quite a number of changes: @@ -95,4 +95,157 @@ number of changes: ``<title>``, plus some HTML-namespaced markup content inside a ``<body>``. -#. +#. Lines 13-32 show a nested XML hierarchy. A ``<folder>`` gets an + ``@name``, making the folder itself published as a possible URL. + It also gets an ``@xml:id``, allowing it to be uniquely addressed + within the entire document. The ``<folder>`` gets a ``<title>`` + child node, but then gets two more ``__getitem__``-enabled child + nodes of type ``<document>``. ("Enabled", as in, has a ``@name`` + attribute.) + +Thus the major changes: + +- The root contains ``<folder>`` nodes which contain ``<document>`` + nodes. + +- The root could contain documents too, and the folder could contain + sub-folders. There's nothing special about the arrangement. + +- The only thing special is the presence of ``@name`` attributes, + which allow ``__getitem__`` (via XPath) to traverse to that child. + +- As we'll see in a second, the ``@xml:id`` allows jumping through the + hierarchy directly to a node. + +- Finally, for UI-framework-ish purposes, having a child ``<title>`` + allows us to show in a browser what we're looking at. + +The ``models.py`` hasn't changed, so let's move to the small changes +in the ``views.py``. + +``views.py`` +============ + +As noted above, we removed the ZPT views, and thus ``views.py`` is +shorter, and focused only on a function that provides an XSLT +template. Although there aren't many lines in that function, there +are some concepts to explain: + +.. literalinclude:: step04/myapp/views.py + :linenos: + +#. We are going to be using a feature from XML called ``xml:id``, + which we explained above in the ``samplemodel.xml`` section. In + lines 4 and 5, we make constants that point to the namespace + needed. + +#. As the comment says, line 9 grabs the "root" node of the site. + Inside this function, we are traversing, node-by-node, through a + hierarchy. Thus our context node is an lxml Element object, which + supports a method to grab the tree (and thus root) in which the + context sits. + +#. Next, to support the XSLT approach we show next, we want to let the + XSLT know the ``xml:id`` that we are sitting on. Since we are + passing it in as an XSLT paramter, we need some special handling: + + - The *value* of the parameter is the ``@xml:id`` of the node we are + sitting on. + + - Per lxml's needs, that value needs to be quoted. Thus, the value + of ``contextid`` will be something like ``"'n1'"`` + +#. Finally, on line 12, we call the XSLT processor, passing in keyword + arguments that become XSLT parameters. Unlike before, the node we + pass in is the top of the tree, rather than the current (context) + node. + +In summary, we render the XSLT by handing it the root node of the +tree, plus a flag that says which node we are currently sitting on. + +``xsltview.xsl`` +================= + +The XSLT template gets the most substantive changes, as we both have +to support this root-and-contextid idea, as well as some features that +put this to use: + +- Having a common look-and-feel across all pages, along with "rules" + that handle each content type + +- Show the context's ``<title>`` in the same place + +- Show some general information about the node + +The following XSLT accomplishes these features: + +.. literalinclude:: step04/myapp/xsltview.xsl + :linenos: + :language: xml + +#. Line 3 accepts the ``@xml:id`` passed in as a parameter of the XSLT + transformation. This can differ between requests, as different + nodes are traversed. + +#. Line 4 jumps directly to the tree node that has that ``@xml:id`` by + using XPath's ``id()`` function. This is a high-speed lookup, + similar to ``document.getElementById()`` in JavaScript. We then + assign the node to a global XSLT variable, to avoid paying that + price again. + +#. Line 5 gets into XSLT's rule-oriented mumbo-jumbo. This template + rule says: "Match on the root of the tree that was passed in, then + do some work." Think of this as a CSS rule that matches on ``body + {}``. + +#. Lines 7-11 output HTML for the document and ``<head>``. + +#. Line 9 (and line 14) inserts the value of the context node's + ``<title>`` using ``<xsl:value-of>``. + +#. Line 16 does some XSLT mumbo jumbo. It says "Find a rule that + handles the context node, which might be a ``<folder>`` or might be + a ``<document>``." Control is then passed to an ``<xsl:template>`` + that meets the conditions. Once that rule is finished, control + returns to line 17. + +#. Lines 17-42 then format some basic information about the context + node. The HTML generated for this, however, appears *after* the + type-specific handler in the resulting HTML. + +#. Line 46 is an ``<xsl:template>`` rule that handles ``<folder>`` + nodes. It only gets control when something else hands control to it. + + In this case, the rule makes a paragraph then lists the contents of + the folder. + +#. Line 50 checks to see if the folder contains any "publishable" + content. We wouldn't want a heading to appear saying "Folder + Contents" with empty space under it. + +#. Line 53 then iterates over all the child nodes which have an + ``@xml:id``. + +#. Lines 55-57 make an ``<li>`` with an ``<a>`` for each item in the + folder. Inside the ``<xsl:for-each``, the "current" node is the + current item in the iteration. The ``@href`` uses what XSLT calls + "attribute value template" (curly braces) to let the XSLT processor + operate inside an attribute. + +#. Line 63 handles ``<document>`` nodes when handed control. + +#. Line 67 recursively copies the nodes in the ``<document>`` content. + +To recap, this XSLT handles any node passed in, and generates a UI +that can handle the global styling, the navigational elements, and the +content for the current traversal hop. + +Conclusion +===================== + +Though not very much code, this is the basis for a useful amount of +features. A hierarchical website with templating that can handle +global styling and navigation, as well as type-driven templating, all +at reasonable (albeit in-memory) performance. + + diff --git a/docs/tutorials/lxmlgraph/step04/myapp/models.py b/docs/tutorials/lxmlgraph/step04/myapp/models.py index 3c03de1a9..a0d64ef59 100644 --- a/docs/tutorials/lxmlgraph/step04/myapp/models.py +++ b/docs/tutorials/lxmlgraph/step04/myapp/models.py @@ -1,3 +1,5 @@ +import os + from zope.interface import implements from zope.interface import Attribute from zope.interface import Interface @@ -32,7 +34,9 @@ def get_root(environ): parser.set_element_class_lookup(parser_lookup) # Now load the XML file - xmlstring = open("myapp/samplemodel.xml").read() + here = os.path.join(os.path.dirname(__file__)) + samplemodel = os.path.join(here, 'samplemodel.xml') + xmlstring = open(samplemodel).read() root = etree.XML(xmlstring, parser) return root |
