TurboGearsのチュートリアルを日本語訳して実行してみる(その1)

Brian's TurboGears Tutorial超訳*1ししてみる。
英語力は中学生もびっくりな感じなので、後学のために指摘していただける幸いです。

Brian's TurboGears Tutorial
Status: Contributed, Needs updating*2

コンテンツ

  1. TurboGearsのインストール
  2. プロジェクトの骨組みの作成
  3. モデルの構築
  4. コントローラの構築
  5. リソーステンプレートの適用
  6. 独自テンプレートの作成
  7. フォーム処理
  8. 非同期JavaScriptの追加
  9. 高度な機能
  10. ライセンス


TurboGearsプロジェクトを始めましたか?
このページにはTurboGearsを知るための必要な情報があります。
ここではユーザ認証のフレームワークや自動生成のForm機能、バリデートのような詳細な機能については述べません。代わりに、このチュートリアルでは、TurboGearsの基本的な振る舞いについて、深く理解することに焦点を当てます。

チュートリアルのアプリケーションはマルチユーザToDo リストマネージャです。筆者はこのプロダクトの最終版を自分のWEBサーバで運営しています。

http://exogen.case.edu:8001

プログラムソースはここからダウンロードできます。サンプル*3

*1:超訳とは必要なキモとなる箇所だけを訳すこと。自分で造語した

*2:ステータスがアップデートが必要となってるのはちょっと気になるな。。。

*3:ただし、原文のソースは古いため修正の必要あり。

TurboGearsのインストール

このチュートリアルはTurboGears1.0b1を対象(cover)としています。
Note:ファイル名は最新のバージョンでは違っているかもしれません。基本的にはバージョンがまたがっても動くでしょう。

もしsetuptoolsをインストールしているならば、次のようにします。

$ easy_install -Uf TurboGears

もしなければ、下の箇所(URL)で取得します。
http://www.turbogears.org/download/


適切な場所でtg-adminを実行します。*1

$ tg-admin

出力結果はこのようになります。

TurboGears 1.0b1 command line interface
Usage: /usr/local/bin/tg-admin [command] [options]
Available commands:
...

Note: パッケージのアルファ版やRC版がリリースされてます。setuptoolsはパッケージのよくないバージョンを取得する可能性があります。もっとも起きそうな結果は、いくつかのステップ中のImportErrorかAttributeErrorです。もし起こったなら、TurboGearsメーリングリストをチェックしてください。これらは、たいてい簡単に解決できます。http://groups.google.com/group/turbogears

*1:この場合TurboGearsのPATHが通っているHOMEディレクトリなどを指す。

プロジェクトの骨組みの作成

次のように入力してプロジェクトを作成します。

$ tg-admin quickstart

このチュートリアルではプロジェクトを'tutorial'と呼びます

Enter project name: tutorial
Enter package name [tutorial]:
...

tg-admminによって作られたディレクトリに移動します。これから先、チュートリアルの間でのいくつかの参照パスは、このディレクトリの相対関係です。

$ cd tutorial/

この場所はプロジェクトのオペレーション場所です。これはビルトインのウェブサーバを利用するケースだと理解してください。

$ python start-tutorial.py

すべて動いたならば、ブラウザでhttp://localhost:8080にアクセスしてください。そしてwelcomeメッセージを確認してください。もし、うまくいかない場合、おそらくTurboGearsのインストールに失敗しています。

ビルトインのサーバのポート番号を変えるにはdev.cfgを編集します。

server.socket_port = 8080

To use the default 'production' configuration, specify which configuration file to use when starting the server:
WEBサーバの起動時に明確にコンフィグレーションファイルを利用するには以下のようにします。

$ python start-tutorial.py prod.cfg

モデルの構築

まず最初にTurbogearsが利用するデータベースカスタマイズをします。もし、データベースを利用しないならば次のセクションに行きます。

"dev.cfg"を開いてあなたが使うデータベースの適切なコメントラインの場所を探します。
このチュートリアルではSQLiteを利用します。理由はサーバコネクトの代わりにローカルファイルを利用するので操作が容易だからです。

sqlobject.dburi = "sqlite:///home/brian/tutorial/tutorial.sqlite"

Note SQLiteを利用する場合はURI絶対パスである必要があります。もちろんSQLiteがインストールされている必要もあります。

"dev.cfg"を閉じます。チュートリアルの終わりまで、これを変更することはないです。

データベーステーブルを定義します。既にある tutorial/model.py のファイルを開きます。
TurboGearsはデータベースの抽出にSQLObjectを利用します。SQLObjectの中では、クラスはテーブルをあらわし、クラスのアトリビュートはテーブルの列をあらわします。
To-Doリスト アプリケーションを作成するわけですから、tutorial/model.pyに次のようなクラスを加えます。

class User(SQLObject):
    email = StringCol(alternateID=True)
    lists = MultipleJoin('List')

class List(SQLObject):
    title = UnicodeCol(notNone=True)
    user = ForeignKey('User')
    items = MultipleJoin('Item')

class Item(SQLObject):
    value = UnicodeCol(notNone=True)
    list = ForeignKey('List')

このモデル内では、ユーザはユニークなE-mailアドレスと複数のリストを持つことができる。リストは表題を持ち、一つのオーナーと複数のアイテムを持つ。アイテムは値とそれらを含むリストです。

テーブルの定義と利用のために、SQLObjectのドキュメンテーションを参照します。

http://sqlobject.org/SQLObject.html

dev.cfg内にTurboGearsがデータベースで使う新しいテーブル定義の情報があります。
テーブルの作成には次を実行します。

$ tg-admin sql create

エラーが出なければテーブル作成は成功です。

テーブル定義に変更が必要な場合、現在のデータベースのものを捨てます。そして、テーブルを再作成します。*1

$ tg-admin sql drop  <--- DB削除
$ tg-admin sql create <---DB作成

テーブルを作り終えた後、プロジェクトにあるPython Shellでインタラクティブに行の追加をテストします。
Shellをスタートさせます。

$ tg-admin shell

トランザクション内でデータを追加か修正をします。コミットなしでシェルを抜けるとデータベースには保存されません。
トランザクションのコミットには、データを変更後、シェルで次のようにタイプします。

>>> hub.commit()

インタラクティブなシェルを使ってテーブルをテストします。順序に従ってデータベースにいくつかのアイテムをinsertをします

>>> u1 = User(email="exogen@gmail.com")
>>> u1.email
'exogen@gmail.com'

>>> u1.id
1

>>> u1.lists
[]

>>> l1 = List(title="Groceries", user=u1)
>>> l1.title
u'Groceries'

>>> l1.user
<User 1 email='exogen@gmail.com'>

>>> l1.user.email
'exogen@gmail.com'

>>> l1.items
[]

>>> u1.lists
[<List 1 title=u'Groceries' userID=1>]

>>> i1 = Item(list=l1, value="Milk")
>>> i2 = Item(list=l1, value="Eggs")
>>> i3 = Item(list=l1, value="Bread")
>>> [item.value for item in l1.items]
[u'Milk', u'Eggs', u'Bread']

>>> len(l1.items)
3

セーブするためにコミットすることを忘れないでください。

>>> hub.commit()

テーブルが十分に動いたなら、model.pyを閉じてください。

今後、チュートリアルの終わりまで変更する必要はないです。

*1:tg-admin sql createでは、既にtableがある場合でもエラーなく実行できる。データが入っていた場合にはどうなるんだろ??

コントローラーの構築

コントロラーは、リクエストがウェブサービスに来たときにコードを取得して実行する記述の方法です。
Python関数へリクエストURLとパラメータの一致して TurboGearsCherryPyを呼び出して利用します。

tutorial/controllers.pyを開き、追記と修正を始めます。

contorollers.pyのRoot classを下のコードで置き換えたら、TurboGearsの"Hello, world!"相当のアプリケーションができます。

class Root(controllers.RootController):
    @turbogears.expose()
    def index(self):
        return "Hello, world!"

もう一度サーバをスタートさせて、ブラウザでhttp://localhost:8080へアクセスします。すると、テキストの中に"Hello, world!"が表示されます。

どうですか、動いていますか?

RootコントローラーはウェブサービスのルートURL(/)に相当します。/ のリクエストを受け取ったら、TurboGearsはRoot Controllerのiindex メソッドを見ます。indexメソッドはウェブディレクトリのindex.htmlに相当します。

publicリソース(expose)を作ります。exposeはturbogears.exposeに付随のディレクターです。*1

@turbogears.expose() 

コード内のexposedメソッドはリソースを受け取った時に実行されます。Rootコントローラにはたくさんのresourceを追加することができます。

@turbogears.expose()
def about(self):
    return "This tutorial was written by Brian Beck."

ブラウザでhttp://localhost:8080/about をアクセスしてください。

開発中はWEBサーバの再起動なしにコントローラーを作り替えることが可能です。

リクエストはパラメータも持っています。GETリクエストのURLやエンコード済みのフォームinput内のPOSTリクエストがあります。
両方のメソッドはどちらもコントローラでは同じ方法です。

@turbogears.expose()
def about(self, author="Brian Beck"):
    return "This tutorial was written by %s." % author

/aboutのリクエストではのテキストメッセージが出てきます。しかし、レスポンス /about?autor=Napoleon の表示は次のようになります。

"This tutorial was written by Napoleon."


pathコンポーネントを加えます。
resourceを含んだクラスインスタンスをRootコントローラのアトリビュートに加えます。
ここに"users"pathコンポーネントをRootコントローラへ加えます

import model

class Users:
    @turbogears.expose()
    def index(self):
        users = model.User.select()   ※ import model が必要
        return ", ".join(user.email for user in users)

class Root(controllers.RootController):
    users = Users()
    ...

/users/indexか/usresにブラウズするとデータベースの中のusersリストが表示されます。

コントローラーの構造はCherryPyのドキュメントを参照します。

http://www.cherrypy.org/wiki/CherryPyBook

いくつかのuserは自分自身のリソースをUsersコントローラの中に持っています。
しかし、この要求はデータベース内のすべてのユーザのメソッドに加えることとなる。
幸運にも、これらはダイナミックpathコンポーネントで簡単に解決できる。リソース要求があったとき、対応したコントローラーが見つけることができない。呼び出されたdefaultメソッドはリクエストリソースの名前を引数(argument)として呼び出されます。

class Users:
    ...

    @turbogears.expose()
    def default(self, userID):
        try:
            userID = int(userID)
        except ValueError:
            return "Invalid user ID!"

        try:
            user = model.User.get(userID)
        except model.SQLObjectNotFound:
            return "No such user!"
        else:
            return user.email

ブラウザで/user/1にリクエストするとID 1のユーザのe-mailアドレスが表示されるか、IDが見つからなかった場合に"No such user!"と表示されます。/users/dogをリクエストすると通常tableのIDはIntegersなので"invalid user ID!"と表示されます。

とりあえず今日はここまで。
残りは次回に続く。。。のか???

*1:from turbogears import expose を記述してると@expose()だけでよい