第2回 ビュークラスとZPTを使って簡易RSSリーダ作成

田原 悠西

2008/9/11

動的なビューを作るプログラム

 ここまでで静的なビューの作り方が分かったと思います。次に動的なビュー、つまりHTMLを動的に作るプログラムを書いてみましょう。実のところ、CSSのパスを生成するときにも動的な処理が行われていますが、次はアプリケーションのロジックを作ってみます。

 Zope 3で動的にHTMLを生成するには、Zope Page Template(ZPT)とビュークラスを使います。

 ビュークラスはPythonで記述して作るので、表示に使いたいデータ構造を組み立てる複雑なロジックはここに書きます。ZPTは、出力したいHTMLとXMLをほとんどそのまま書けるテンプレートなので、ビュークラスのメソッドで組み立てたデータ構造をHTMLやXMLにはめ込むのに使います。

 試しにビュークラスとZPTを使って簡易RSSリーダを作ってみましょう。初めに肝心のロジックから作ります。方針としては、RSSのエントリをPythonの辞書で表現することにして、ビュークラスに辞書のリストを返すメソッドを実装することにしましょう。

 ZPTでは、辞書のリストを繰り返し処理でHTMLに埋め込むことにします。ビューの機能を作るコツは、ZPTで扱いやすい単純なデータ構造をメソッドで作ってあげることです。そうすれば、テンプレートのメンテナンスが楽になるので、後でHTMLのデザインを変えたくなってもすぐに対応できます。

 さて、いったんZopeのことは忘れて、RSSを読み込んで一連のエントリを辞書のリストに変換するPythonプログラムを考えてみましょう。

 対話プロンプトを起動します。

$ python2.4
Python 2.4.4 (#2, Oct 20 2007, 22:05:51)
[GCC 4.1.2 20070302 (prerelease) (4.1.2-1mdv2007.1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

 RSSをインターネットから取得することを考えてurllibモジュールを使ってみました。

>>> import urllib
>>> url = urllib.urlopen('http://www.zope.org/Planet/planet_rss10.xml')
>>> url
<addinfourl at 47365242360824 whose fp = <socket._fileobject object at 0x2b1411c58050>>
>>> data = url.read()
>>> data[:50]
'<?xml version="1.0"?>\n\n<rdf:RDF\nxmlns:rdf="http://'

 うまくXMLフォーマットのデータを取得できました。次にこれをパースしましょう。手軽な方法がよかったので、今回はxml.dom.minidomモジュールを使ってみました。

>>> import xml.dom.minidom
>>> document = xml.dom.minidom.par
xml.dom.minidom.parse xml.dom.minidom.parseString
>>> document = xml.dom.minidom.parseString(data)
document.getElementsByTagName document.getElementsByTagNameNS
>>> len(document.getElementsByTagName('item'))
15
>>> item = document.getElementsByTagName('item')[0]
>>> len(item.childNodes)
13
>>> [i.nodeType for i in item.childNodes]
[3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3]
>> xml.dom.minidom.Node.E
xml.dom.minidom.Node.ELEMENT_NODE
xml.dom.minidom.Node.ENTITY_NODE
xml.dom.minidom.Node.ENTITY_REFERENCE_NODE
>>> xml.dom.Node.ELEMENT_NODE
1
>>> xml.dom.Node.TEXT_NODE
3

 DOMツリーにXMLファイルの改行やスペースが交ざっていました。これは不要なので取り除きます。

>>> len([i for i in item.childNodes if i.nodeType==xml.dom.Node.ELEMENT_NODE])
6
>>> i = [i for i in item.childNodes if i.nodeType==xml.dom.Node.ELEMENT_NODE][0]
>>> i.nodeName
u'title'
>>> i.childNodes[0]
<DOM CDATASection node "Repozecast...">
>>> i.childNodes[0].data
u'Repozecast Number 1'
>>>

 RSSのエントリのタイトルを取得できました。これでどの機能を使えばいいのか大ざっぱに分かりました。これを踏まえて、ビュークラスを追加したのが下の新しいapp.pyです。

import grok
import urllib
import xml.dom.minidom

class Sample(grok.Application, grok.Container):
    pass

class Index(grok.View):
    pass # see app_templates/index.pt

class HelloWorld(grok.View):
    pass

class RSSViewer(grok.View):

    def getEntries(self):
        data = urllib.urlopen('http://www.zope.org/Planet/planet_rss10.xml').read()
        document = xml.dom.minidom.parseString(data)
        result = []
        for item in document.getElementsByTagName('item'):
            entry = {}
            for node in item.childNodes:
                if node.nodeType==xml.dom.Node.ELEMENT_NODE:
                    if len(node.childNodes):
                        entry[node.nodeName] = node.childNodes[0].data
            result.append(entry)
        return result

ビュークラスを使うZPTを追加する

 次に、上で実装したビュークラスを使うZPTを追加しましょう。ビュークラスの名前がRSSViewerなので、全部小文字にして拡張子.ptを付けたrssviewer.ptをapp_templatesディレクトリに作ります。

<html>
<head>
</head>
<body>
<h1>RSS Viewer</h1>
<div tal:repeat="entry view/getEntries">
  <h2>
    <a href=""
        tal:attributes="href entry/link"
        tal:content="entry/title">
    </a>
  </h2>
  <div tal:replace="structure entry/description"></div>
  <div tal:content="python:entry.get('dc:date')"></div>
</div>
</body>
</html>
app_templates/rssviewer.pt

 tal:repeatは、「このタグを繰り返します」という意味の命令です。ここでは<div tal:repeat="entry view/getEntries">と書いているので、divタグを繰り返し処理します。右のview/getEntriesが繰り返す要素を取得する命令で、このテンプレートのビュー(つまり、RSSViewerインスタンス)のgetEntriesメソッドを呼び出すという意味になります。

 左のentryはこのループ変数の名前です。つまり、getEntriesの返り値のリストの要素1つ1つがループごとにentryという名前の変数に束縛されます。この変数はtal:repeatが使われているタグ、この場合はdivタグで囲われた中で有効になります。このテンプレートでは、getEntriesが辞書のリストを返すので、entryは必ず辞書です。

 tal:attributesは先にCSSファイルを埋め込むときに使いました。タグの属性を動的に生成する命令です。ここでは、entry/linkとあり、entryは必ず辞書なので、辞書entryのキーがlinkである値をhrefの属性の値にします。

 tal:contentはこの属性のあるタグで囲った中身を動的に置き換える命令です。entry/titleとあるので、辞書entryのキーがtitleである値をaタグの中に挿入します。

 tal:replaceはcontentと似ていますが、この属性のあるタグごと動的に置き換えます。つまり、tal:replaceが書いてあるタグ自身も置き換えられてなくなってしまうということです。

 また、tal:replaceの中では式の初めにstructureと書いてあります。これを書いておくと、置き換えに使われた文字列にHTMLの特殊文字が入っていてもエスケープされません。例えば、<html>は&lt;html&gt;とならず、<html>がそのままHTMLに埋め込まれます。

 ZPTは標準で安全側に倒して設計されているため、structureと明示的に書かない限り、動的に挿入される文字列は必ずエスケープされます。今回は、RSSの中にHTMLが入っていることが多いので、structureを使っています。

 最後のtal:contentの式では、python:と書いてあります。これはPythonの式を使いますという宣言です。それで、python:の後にはentry.get('dc:date')のようにPythonの式が書かれています。

 やっていることは上のtal:contentと同じで辞書entryのキーdc:dateを取得しているだけなのですが、entry/dc:dateと書いた場合に、コロンのせいで別の意味に解釈されてしまうため、わざとPython式を使っています。

2/3

Index
ビュークラスとZPTを使って簡易RSSリーダ作成
  Page1
プロジェクトディレクトリの確認
ひな型のアプリケーションの仕組み
ビューの追加
Page2
動的なビューを作るプログラム
ビュークラスを使うZPTを追加する
  Page3
Webブラウザからの入力に対応させる

Zope 3とは何ぞや?

 Coding Edgeお勧め記事
いまさらアルゴリズムを学ぶ意味
コーディングに役立つ! アルゴリズムの基本(1)
 コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう
Zope 3の魅力に迫る
Zope 3とは何ぞや?(1)
 Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか?
貧弱環境プログラミングのススメ
柴田 淳のコーディング天国
 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く?
Haskellプログラミングの楽しみ方
のんびりHaskell(1)
 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう
ちょっと変わったLisp入門
Gaucheでメタプログラミング(1)
 Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間