2007年11月4日

Pylonsで階層的なRestControllerを書く


Pylonsは、コントローラーを作るときに、pasterコマンドでコードのベースを生成します。
普通のコントローラーとRestなコントローラーと二種類作成できます。



普通のコントローラーは、routing.pyの中で、URLへのマッピングを書きますが、Restコントローラーは、resourceメソッド一発で、CRUDに必要なマッピングをすべて設定してくれます。



例えばproject用のRestコントローラーを生成してマッピングすると以下のようになります。

$ paster restcontroller project projects

routes.py

mapper.resource('project', 'projects')


マッピング


/projects/index

一覧など。

/projects

GETの場合は、indexと同じ。POSTの場合は、新規projectを保存

/projects/new

新規登録フォーム

/projects/:id
GETの場合idで指定されたprojectの詳細,PUTで更新、DELETEで削除

/projects/{:id};edit idで指定されたprojectの編集フォーム





あるリソースの下に紐付くリソースも存在します。
オブジェクト指向でいうとコンポジションでしょうか。
projectの例で考えてみると、タスク(task)などがあるでしょう。



project内のtaskについて、Restコントローラーを設定するには以下のようにします。

$ paster restcontroller task tasks

routes.py

mapper.resource('task', 'tasks', parent=dict(member_name='project', collection_name='projects')

このようにすると、projectリソースの下にtaskリソースが存在するようにマッピングされます。
/projects/:id/tasks/index のように、/projects/:id 以下に、task用のURLがマッピングされます。



Pylonsはテンプレート内で、コントローラーへのリンクを貼る場合には、WebHelperを使います。
上記、projectへのリンクは、
h.url('project', id=c.project.id)
などとします。
taskのように、parentを設定している場合は、以下のようになります。

h.url('project_tasks', project_id=c.project.id) # /project/:project_id/tasks
h.url('project_task_new', id=task.id, project_id=c.project.id) # /project/:project_id/tasks/:id/new

上位のリソースに対するidは、リソース名 + _id という名前になりますね。



ここで、指定されているproject_idは、引数で受け取れます。

def index(self, format='html', project_id=None):
"""GET /tasks: All items in the collection."""
# url_for('tasks')

また、Routesがenvironに格納するwsgiorg.routing_argsからも取得できます。

project_id = request.environ['wsgiorg.routing_args'][1]['project_id']



多くの場合、モデルも同様の階層となるような関連となっているため、がんばれば自動化できるかもしれません。