Pylons/PyramidユーザーのためのZope2

Zopeは、それをモノにしたときのすばらしい悟り体験のために勉強しましょう

—How To Become A Pythonista

嘘です。

まあでも、Zopeを実戦で使うことはなかろうとも、そのコンセプトを知るのは無駄ではないでしょう。 特にPyramidはrepozeというZopeとWSGIの合流を目的にしたプロジェクトが出自なので、Zopeからのアイディアも多く採用されています。 Pyramidを使っていてZopeを再発見することも少なからずあるようなので、今更ながらでもZope2で遊ぶガイドとして、なんか書いてみようと思います。

Zopeとは?

Zopeの言葉がさまざまに利用されているため、どれがZopeなのかわかりにくくなっています。 だいたい以下のものがZopeな気がします。

  • Zope2
  • Zope Corporation
  • 過去にZope3と呼ばれていたBluebreamというフレームワーク

ここではZope2だけを対象にします。

Zope3はコンポーネントに分解され、汎用コンポーネントはZope Toolkitに、管理画面などのアプリケーション部分は Bluebreamになりました。 Zope Toolkitは Bluebreamも使っていますが、Zope2やgrok, Ploneなどが使うようになっています。

Zope2はフレームワークではなく、アプリケーションサーバーです。 開発者はZope2上にアプリケーションとなるクラスやオブジェクトを登録して、WebアプリケーションをZope2アプリケーションサーバー上で実行させることになります。

buildout

Zope2に限らずZope Toolkitを使うプロジェクトは多くの場合 zc.buildout を使って開発環境を構築します。

zc.buildoutは、環境構築の手順をパッケージにしたrecipeを使って環境構築を行います。 Zope2の環境構築には、 plone.recipe.zope2instance がよく用いられます。

buildoutによる環境構築は以下のような手順になります。

# bootstrap.py を実行してbuildoutを使えるようにする # buildout.cfg でレシピなどの設定をする # bin/buildout でbuildout.cfgで設定された内容を実行する

Zope2の環境を構築する

buildout.cfgを書く

まずは環境構築のためにbuildout.cfgを書きましょう。

buildout.cfg:

[buildout]
extends = http://download.zope.org/Zope2/index/2.13.21/versions.cfg
parts = zope2
        instance
interpreter = zopepy

[zope2]
recipe = zc.recipe.egg
eggs = Zope2

[instance]
recipe = plone.recipe.zope2instance
eggs = ${zope2:eggs}
user = admin:admin
http-address = 8080

buildoutでは、このファイルの各セクション(buildout, zope2, instanceなど)をpartといいます。 buildoutパートでは、extends, partsを指定しています。 buiildoutの設定ファイルは継承可能なので、extendsで他の設定ファイルを指定すると、その設定を受け継ぐようになります。 この例では Zope2 2.13.21で利用するライブラリバージョンなどが受け継がれます。 (気になる人は実際に http://download.zope.org/Zope2/index/2.13.21/versions.cfg をダウンロードして中を見てみましょう)

zope2パートでは必要なライブラリを設定しています。 zc.recipe.eggは、ライブラリをまとめて、それらのライブラリをPYTHONPATHに含めるなど、virtualenvとpipを合わせたような環境作成を行います。 interpreterで指定したzopeppyという名前のコマンドが作成されます。 そのコマンドを実行すると、eggsで指定したライブラリが利用可能なpythonインタプリタが起動されます。 ひとまずZope2を動かすだけなので、必要なのはZope2です。

instanceパートではZope2の実行環境構築の設定をしています。 eggsでは、 zope2パートのeggsと同じ内容とするため、 ${zope2:eggs} というプレースホルダーを使っています。 他にもオプションの設定がありますが、とりあえずZope2アプリケーションサーバーを立ち上げるにはこれで十分です。

bootstrapして、buildoutして..

buildoutするには、zc.buildoutをインストールしますが、site_packagesなどにはインストールせずに、プロジェクト直下にインストールします。 そのためのスクリプトが bootstrap.py です。 http://downloads.buildout.org/1/bootstrap.py からダウンロードしてきましょう。 bootstrap.pyを先ほど作成したbuildout.cfgと同じディレクトリにコピーします。 また、既存のPython環境から隔離するため、virtualenvを使います。

以下のように、実行します。

$ virtualenv env
$ env/bin/python bootstrap.py
$ bin/buildout

Zope2を立ち上げる

buildoutでZope2の環境ができあがりました。 plone.recipe.zope2instance で instance コマンドが作成されています。 instance fg でフォアグランド実行ができます。 ひとまずこれで立ち上げて動作を確認してみましょう。

$ bin/instance fg

http://localhost:8080 でZope2が実行されているのがわかります。 management interface(ZMI) にはbuildoutで設定した admin ユーザーでログインできます。

ZMIの使い方はここでは説明しませんが、 Zope2 Book などが参考になるでしょう。

Zope2プロダクト開発

Zope2上のアプリケーションは過去に Products フォルダに入れなければなりませんでした。 現在ではそのような制限はなくなりましたが、互換性のためにProducts名前空間を使っているZope2アプリケーションも多くあり、総じてZope2アプリケーションはプロダクトと呼ばれます。

Zope3スタイルとfive.grok

Zope2はすでにZope Toolkitを使っているので、その中で動かすアプリケーションでも利用できます。 便宜上Zope3スタイルと呼んでおきます。

Zope2の伝統的なアプリケーションでは、オブジェクトが直接ビューを返すメソッドを持っています。 Zope3スタイルではこれを分離して扱います。

Zope3スタイルでそのまま実装しようとすると、configure.zcmlに多くのコンポーネント設定を書くことになります。 GrokというZope3フレームワークがあり、これは、configure.zcmlに書くことなく、規約でコンポーネントを構成しています。 この技術をさらにZope2上で利用できるようにしたものがfive.grokです。 (Zope3の技術をZope2にバックポートするものは Five という名前空間を使うのが慣例です。)

プロジェクトの作成

Zope2プロダクトと言えども、Pythonパッケージであることは変わりません。 ただし、プロダクトごとに細かくパッケージを分けたりするので、 mr.developer で管理してみましょう。 mr.developerを使うと、開発中パッケージをリポジトリから取り寄せたり、developの対象から外したりできます。

プロジェクト直下にsrcディレクトリを作成します。 これから作成するguestbookアプリケーションのためのディレクトリを作成して、setup.pyを書きましょう。

$ mkdir -p src/guestbook

src/guestbook/setup.py:

from setuptools import setup, find_packages

requires = [
    "Zope2",
    "five.grok",
]

setup(name="guestbook",
      install_requires=requires,
      packages = find_packages()
)

mr.developerをbuildoutに追加します。

buildout.cfg:

[buildout]
extensions = mr.developer

[sources]
guestbook = fs guestbook

guestbookをinstanceのeggsに追加します。 直接buildout.cfgに追加してもよいですが、guestbook用の設定ファイルを新しく作成して、元のbuildout.cfgを拡張するようにしてみましょう。

guestbook.cfg:

[buildout]
auto-checkout = guestbook

[zope2]
eggs += guestbook

buildoutで、buildout.cfg以外の設定ファイルを使う場合は -c オプションを使います。

$ buildout -c guestbook.cfg

これでguestbookアプリケーションを開発する準備が整いました。

Guestbookアプリケーション

Zope2のアプリケーションは単にZODB上に保存されるオブジェクトです。 が、ZMIから追加できるように登録したり、コンポーネントの構成を登録したりとめんどうが多いので、five.grokを利用してさっくりといきましょう。

まずは guestbookプロジェクトにguestbookパッケージを作ります。

$ mkdir -p src/guestbook/guestbook
$ touch src/guestbook/guestbook/__init__.py

app.py にアプリケーションを実装します。

src/guestbook/guestbook/app.py:

from datetime import datetime
from five import grok
from persistent.list import PersistentList

class Guestbook(grok.Model):
    meta_type = 'guestbook'

    def __init__(self, *args, **kwargs):
        super(Guestbook, self).__init__(*args, **kwargs):
        self.greetings = PersistentList()

    def add_comment(self, name, comment):
        greeting = dict(name=name, comment=comment, create_at=datetime.now())
        self.greetings.append(greeting)

add_comment メソッドがゲストブックに情報を書きこむメソッドです。 オブジェクトデータベースなので、これだけで情報は保存されます。 meta_typeはZope2のZMI上で表示される文字列です。 あとは、ビューが必要です。 これらも app.py に実装しましょう。 モデルと同じモジュール内でビューを実装すると、モデルとビューの対応付けが自動で行われます。

src/guestbook/guestbook/app.py 後半:

class Index(grok.View):
    pass

class Post(grok.View):
    def update(self, name, comment):
        self.context.add_comment(name, comment)

    def render(self):
        return self.redirect(self.url(self.context))

Indexクラスは、単にcontextを表示するだけなので、なにもメソッドを持ちません。 Postクラスはフォームの入力を受けて、Guestbookのadd_commentを呼び出すビューです。 updateメソッドで実際の処理を行い、renderメソッドで元の画面にリダイレクトしています。

テンプレート

ビュークラスは、HTMLの生成は受け持っていません。 Postクラスではrenderメソッドをオーバーライドしているため、レスポンスボディの生成されないため、IndexクラスのHTMLテンプレートだけが必要です。

five.grokではgrok.Viewを継承している場合、 {モジュール名}_templates/{クラス名}.pt というテンプレートが自動で利用されます。 appモジュールの Indexクラスの場合は app_templates/index.pt となります。

テンプレートの内容は https://github.com/aodag/zope2-guestbook/blob/master/guestbook/app_templates/index.pt をみてください。

configure.zcml

さて、アプリケーションの実装ができたので、これらをコンポーネント登録します。 Zope3スタイルでは上記のクラスを全部configure.zcmlで登録することになりますが、five.grokを使っているので、1行のディレクティブで済ませることができます。

src/guestbook/guestbook/configure.zcml:

<configure xmlns="http://namespaces.zope.org/five"
           xmlns:grok="http://namespaces.zope.org/grok">
  <include package="five.grok"/>
  <grok:grok package="."/>
</configure>

ZMIに追加する

アプリケーションを実装して、コンポーネントへの登録もできました。 あとは、ZMIへの登録です。

ZMIはZope2固有の機能なので、それに従わなくてはなりません。

src/guestbook/guestbook/__init__.py:

from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from . import app


def addGuestbook(context, id, title):
    """ add new guestbook """
    guestbook = app.Guestbook(id)
    guestbook.title = title
    context._setObject(id, guestbook)
    return "OK"

manage_addGuestbookForm = PageTemplateFile('manage_addGuestbook', globals())

def initialize(registrar):
    registrar.registerClass(
        app.Guestbook,
        constructors=(manage_addGuestbookForm,
                      addGuestbook))

アプリケーションクラスと、追加するためのフォームとアクションをregistrarで登録します。 後はこの関数を configure.zcml に追加します。

src/guestbook/guestbook/configure.zcml 最終形:

<configure xmlns="http://namespaces.zope.org/five"
           xmlns:grok="http://namespaces.zope.org/grok">
  <registerPackage package="." initialize=".initialize"/>
  <include package="five.grok"/>
  <grok:grok package="."/>
</configure>

ZMIからの追加

ここまできたら Zope2 を再起動します。 ZMIからguestbookを追加すれば、Guestbookアプリケーションを利用できます。

まとめ

めんどい

Comments !

blogroll

social