summaryrefslogtreecommitdiff
path: root/docs/tutorials/lxmlgraph/step02.rst
diff options
context:
space:
mode:
authorPaul Everitt <paul@agendaless.com>2008-07-24 17:14:16 +0000
committerPaul Everitt <paul@agendaless.com>2008-07-24 17:14:16 +0000
commit1a7b14de4bc89ef0fd162ef8e62d01926e42b54e (patch)
tree8d9ccc8187ac29eb80ac310c6f82c17d548cdf54 /docs/tutorials/lxmlgraph/step02.rst
parentc1a6ef6f751a16b7a352bc3fdad13d4b5b73f212 (diff)
downloadpyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.tar.gz
pyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.tar.bz2
pyramid-1a7b14de4bc89ef0fd162ef8e62d01926e42b54e.zip
Add tutorial sections
Diffstat (limited to 'docs/tutorials/lxmlgraph/step02.rst')
-rw-r--r--docs/tutorials/lxmlgraph/step02.rst118
1 files changed, 118 insertions, 0 deletions
diff --git a/docs/tutorials/lxmlgraph/step02.rst b/docs/tutorials/lxmlgraph/step02.rst
new file mode 100644
index 000000000..d4b6b261b
--- /dev/null
+++ b/docs/tutorials/lxmlgraph/step02.rst
@@ -0,0 +1,118 @@
+================================================
+Step 02: Hello World as XML
+================================================
+
+We now have a website with ``/a`` and ``/b`` URLs. Each has a default
+view that returns a teensy weensy response.
+
+In this step we will do the exact some scope, but using an XML
+document as our model data. We will leverage the same ``repoze.bfg``
+machinery:
+
+ - Model data with interfaces that define "types"
+
+ - ZCML configuration to provide type-specific views
+
+We do, however, need to do some things differently:
+
+ - Our model class needs to use lxml to inject itelf into the XML
+ nodes
+
+ - That model class needs to implement the "handshake"
+
+Let's look at what changed.
+
+File ``myapp/samplemodel.xml``
+--------------------------------
+
+Our hierarchy in Step 01 was very simple. Mimicking it in XML is,
+thus, also very simple:
+
+.. literalinclude:: step02/myapp/samplemodel.xml
+ :linenos:
+ :language: xml
+
+#. Line 2 provides the root of the model as an XML ``<site>`` node.
+ The element name doesn't have to be ``<site>``.
+
+#. In lines 3-4, the ``<site>`` contains 2 top-level children: a and
+ b. These are provided as an element name ``<document>``. This,
+ also, is meaningfless as far as ``repoze.bfg`` is concerned.
+ However, this is where you compose th information model you are
+ publishing.
+
+The only special constraint is that a node that wants to be "found" by
+``repoze.bfg`` in during traversal *must* have an ``name`` attribute.
+(The use of ``@name`` corresponds to ``__name__`` in the
+``repoze.bfg`` handshake.) Each hop in the URL tries to grab a child
+with an attribute matching the next hop. Also, the value of the
+``@name`` should be unique in its containing node.
+
+
+Module ``myapp/models.py``
+------------------------------
+
+Here is the serious change: we have made an XML-aware model. Or is it
+a model-aware XML document? Such questions, harrumph.
+
+At a high level, we make write a class that "extends" lxml Element
+nodes, create an lxml parser, and register the custom class with the
+parser.
+
+.. literalinclude:: step02/myapp/models.py
+ :linenos:
+
+#. Line 4 imports lxml.
+
+#. Line 9 creates the custom class we are going to use to extend
+ etree.ElementBase. The lxml website has great documentation on the
+ various ways to inject custom Python behavior into XML.
+
+#. Just as before, line 12 says that instances of this class support a
+ certain content type (interface.) In our case, instances will be
+ XML nodes.
+
+#. ``repoze.bfg`` has a "protocol" where model data should have an
+ ``__name__`` attribute. Lines 14-16 implement this by grabbing the
+ ``@name`` attribute of the current node.
+
+#. URL traversal in ``repoze.bfg`` works via the ``__getitem__``
+ protocol. Thus, we need a method that implements this. Lines
+ 18-26 use XPath to look for a direct child that has an ``@name``
+ matching the item name that's being traversed to. If it finds it,
+ return it. If not, or if more than one is found, raise an error.
+
+#. As before, ``get_root`` is the function that is expected to return
+ the top of the model. In lines 30-32 we do the lxml magic to get
+ the custom Python class registered. We then load some XML and
+ return the top of the tree.
+
+
+Module `myapp/views.py``
+--------------------------
+
+We only made two changes here.
+
+.. literalinclude:: step02/myapp/views.py
+ :linenos:
+
+#. Line 5 grabs the element name (tag name) of the ``context``, which
+ is the current XML node that we're traversing through.
+
+#. Line 6 uses the special property we defined in our custom Python
+ class to get the ``__name__`` of the context.
+
+
+Browsing the Model
+------------------------
+
+We can use the same URLs from Step 01 to browser the model and see
+results::
+
+ http://localhost:5432/a
+ http://localhost:5432/b
+ http://localhost:5432/c (Not Found)
+
+In this case, each request grabs a node in the XML and uses it as the
+data for the view. ``repoze.bfg`` doesn't really know that, unlike
+Step 01, we no longer have "real" Python data. \ No newline at end of file