Skip to content

Commit

Permalink
Added XsltField documentation, README toc, advanced_example
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Jul 6, 2012
1 parent 252b8ae commit 8cf9a24
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 2 deletions.
65 changes: 63 additions & 2 deletions README.md
Expand Up @@ -4,6 +4,31 @@
[lxml](http://lxml.de/)'s XPath and XSLT functionality in a manner resembling
django database models.

## Contents

* [Installation](#installation)
* [Example](#example)
* [Advanced Example](#advanced-example)
* [XmlModel Meta options](#xmlmodel-meta-options)
* [namespaces](#namespacesoptionsnamespaces--)
* [parser_opts](#parser_optsoptionsparser_opts--)
* [extension_ns_uri](#extension_ns_urioptionsextension_ns_uri)
* [@lxml_extension reference](#lxml_extension-reference)
* [ns_uri](#ns_uri)
* [name](#name)
* [XPathField options](#xpathfield-options)
* [xpath_query](#xpath_queryxpathfieldxpath_query)
* [required](#requiredxpathfieldrequired)
* [extra_namespaces](#extra_namespacesxpathfieldextra_namespaces)
* [extensions](#extensionsxpathfieldextensions)
* [XPathSingleNodeField options](#xpathsinglenodefield-options)
* [ignore_extra_nodes](#ignore_extra_nodesxpathsinglenodefieldignore_extra_nodes--false)
* [XsltField options](#xsltfield-options)
* [xslt_file, xslt_string](#xslt_file-xslt_stringxsltfieldxslt_filexsltfieldxslt_string)
* [parser](#parserxsltfieldparser)
* [extensions](#extensionsxsltfieldextensions--)
* [XmlModel field reference](#xmlmodel-field-reference)

## Installation

Because this is a project under active development, the recommended
Expand Down Expand Up @@ -74,6 +99,10 @@ if __name__ == '__main__':
main()
```

## Advanced Example

An example of django-xml usage which includes XsltField and @lxml_extension methods
can be found [here](https://github.com/theatlantic/django-xml/blob/master/docs/advanced_example.md).

## XmlModel Meta options

Expand Down Expand Up @@ -144,8 +173,8 @@ A dict of extra prefix/uri namespace pairs to pass to

#### extensions<br>`XPathField.extensions`

Extra extensions to pass on to
[`lxml.etree.XPathEvaluator()`](http://lxml.de/api/lxml.etree-module.html#XPathEvaluator).
Extra extensions to pass on to the constructor of
[`lxml.etree.XSLT`](http://lxml.de/api/lxml.etree.XSLT-class.html#__init__).
See the [lxml documentation](http://lxml.de/extensions.html#evaluator-local-extensions)
for details on how to form the <b>`extensions`</b> keyword argument.

Expand All @@ -160,8 +189,40 @@ Defaults to `False`.

To return the full list of nodes, Use an <b>`XPathListField`</b>

## XsltField options

#### xslt_file, xslt_string<br>`XsltField.xslt_file`<br>`XsltField.xslt_string`

The first positional argument to XsltField is the path to an xslt file.
Alternatively, the xslt can be passed as a string using the
<b>`xslt_string`</b> keyword argument. It is required to specify one of these
fields.

#### parser<br>`XsltField.parser`

An instance of [lxml.etree.XMLParser](http://lxml.de/api/lxml.etree.XMLParser-class.html)
to override the one created by the XmlModel class. To override parsing options
for the entire class, use the [<b>`parser_opts`</b>](#parser_optsoptionsparser_opts--)
attribute of the [XmlModel internal <b>`Meta`</b> class](#xmlmodel-meta-options).

#### extensions<br>`XsltField.extensions = {}`

Extra extensions to pass on to
[`lxml.etree.XPathEvaluator()`](http://lxml.de/api/lxml.etree-module.html#XPathEvaluator).
See the [lxml documentation](http://lxml.de/extensions.html#evaluator-local-extensions)
for details on how to form the <b>`extensions`</b> keyword argument.

## XmlModel field reference

```python
class XsltField(xslt_file=None, xslt_string=None, parser=None, extensions=None)
```

Field which abstracts the creation of
[lxml.etree.XSLT](http://lxml.de/api/lxml.etree.XSLT-class.html) objects.
This field's return type is a callable which accepts keyword arguments that
are passed as parameters to the stylesheet.

```python
class XPathField(xpath_query, required=False, extra_namespaces=None, extensions=None)
```
Expand Down
130 changes: 130 additions & 0 deletions docs/advanced_example.md
@@ -0,0 +1,130 @@
# Advanced Example

## myapp/xmlmodels.py

```python
import re, time
from datetime import datetime
from os.path import dirname, join
from lxml import etree
from djxml import xmlmodels

strip_namespaces = etree.XSLT(etree.XML("""
<x:stylesheet version="1.0" xmlns:x="http://www.w3.org/1999/XSL/Transform"
xmlns:xhtml="http://www.w3.org/1999/xhtml">
<x:output encoding="utf-8" method="xml"/>
<x:template match="@*|node()"><x:copy><x:apply-templates/></x:copy></x:template>
<x:template match="xhtml:*"><x:element name="{local-name()}"><x:apply-templates/></x:element></x:template>
</x:stylesheet>"""))


class AtomFeed(xmlmodels.XmlModel):

class Meta:
extension_ns_uri = "urn:local:atom-feed-functions"
namespaces = {
"fn": extension_ns_uri,
"atom": "http://www.w3.org/2005/Atom",
}

feed_title = xmlmodels.XPathTextField("/atom:feed/atom:title")

updated = xmlmodels.XPathDateTimeField("/atom:feed/atom:*[%s]" \
% "local-name()='updated' or (local-name()='published' and not(../atom:updated))")

entries = xmlmodels.XPathListField("/atom:feed/atom:entry", required=False)

titles = xmlmodels.XPathTextListField("/atom:feed/atom:entry/atom:title",
required=False)

transform_to_rss = xmlmodels.XsltField(join(dirname(__file__), "atom2rss.xsl"))

@xmlmodels.lxml_extension
def escape_xhtml(self, context, nodes):
return u"".join([etree.tounicode(strip_namespaces(n)) for n in nodes])

@xmlmodels.lxml_extension
def convert_atom_date_to_rss(self, context, rfc3339_str):
try:
m = re.match(r"([\d:T-]+)(?:\.\d+)?(Z|[+-][\d:]{5})", rfc3339_str)
except TypeError:
return ""
dt_str, tz_str = m.groups()
dt = datetime(*[t for t in time.strptime(dt_str, "%Y-%m-%dT%H:%M:%S")][0:6])
tz_str = 'Z' if tz_str == 'Z' else tz_str[:3] + tz_str[4:]
return dt.strftime("%a, %d %b %Y %H:%M:%S") + tz_str


def test():
atom_xml_file = join(dirname(__file__), 'atom_feed.xml')
atom_feed = AtomFeed.create_from_file(atom_xml_file)
rss_feed = atom_feed.transform_to_rss()
print u"\n".join([
u"feed_title = %r" % atom_feed.feed_title,
u"updated = %r" % atom_feed.updated,
u"num_entries = %d" % len(atom_feed.entries),
u"titles = %r" % (u", ".join(atom_feed.titles)), u"",])
print u"rss = %s" % etree.tounicode(rss_feed)
```

## myapp/atom_feed.xml

```xml
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:xhtml="http://www.w3.org/1999/xhtml">
<title>Example Feed</title>
<link rel="alternate" href="http://example.org/"/>
<updated>2012-07-05T18:30:02Z</updated>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>An example entry</title>
<link rel="alternate" href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2012-07-05T18:30:02Z</updated>
<summary type="xhtml"><xhtml:div>Some text.</xhtml:div></summary>
</entry>
</feed>
```

## myapp/atom2rss.xsl

```xml
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:fn="urn:local:atom-feed-functions"
version="1.0" exclude-result-prefixes="atom fn">
<xsl:output encoding="utf-8" indent="yes" method="xml" media-type="application/rss+xml"/>
<xsl:template match="atom:*"/>
<xsl:template match="/atom:feed">
<rss version="2.0">
<channel>
<xsl:call-template name="author" />
<description><xsl:value-of select="atom:title"/></description>
<xsl:apply-templates/>
</channel>
</rss>
</xsl:template>
<xsl:template match="atom:link[@rel='alternate']"><link><xsl:value-of select="@href"/></link></xsl:template>
<xsl:template match="atom:entry/atom:id"><guid><xsl:value-of select="."/></guid></xsl:template>
<xsl:template match="atom:entry"><item><xsl:call-template name="author"/><xsl:apply-templates/></item></xsl:template>
<xsl:template match="atom:summary">
<description>
<xsl:choose>
<xsl:when test="@type='xhtml'">
<xsl:value-of select="fn:escape_xhtml(child::node())"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
</xsl:choose>
</description>
</xsl:template>
<xsl:template match="atom:updated">
<pubDate>
<xsl:value-of select="fn:convert_atom_date_to_rss(string(.))"/>
</pubDate>
</xsl:template>
<xsl:template name="author">
<xsl:variable name="emails" select="atom:email|/atom:feed/atom:author[./atom:email][1]/atom:email" />
<xsl:if test="count($emails) &gt; 0"><author><xsl:value-of select="$emails[1]" /></author></xsl:if>
</xsl:template>
</xsl:stylesheet>
```

0 comments on commit 8cf9a24

Please sign in to comment.