お前誰よ
I aodag
I Atsushi ODAGiri
I 所属
I ビープラウド
I Pylonsproject.jp
Pyramid
I 特徴
I Web アプリケーションフレームワーク
I ドキュメント、テストがしっかりしてるコミュニティ
I 長所
I 押しつけがない
I フレームワーク自体が拡張可能
I コンポーネントで整理された実装とAPI
I 短所
I 押し付けがない
I フルスタックでないので各種ライブラリの知識が必要
I 柔軟性の高さは諸刃の剣でもある
使う理由
I SQLAlchemy と一緒に
I 認証方法や権限設定などがアプリケーションに絡んで複雑
I 保存先がDB でない場合などフルスタックがいらない
I アレがきらいだから
pyramid の歴史
I repoze.bfg 0.1 2008-07-08
I repoze.bfg 1.0 2009-07-05
I repoze.bfg 1.3b1 2010-10-25
I pyramid 1.0a1 2010-11-05
I pyramid 1.0 2011-01-30
I pyramid 1.1 2011-07-22
I pyramid 1.2 2011-09-12
I pyramid 1.3 2012-03-21
I pyramid 1.4 2012-12-18
I pyramid 1.5 2014-05-31
インストール
$ pyvenv.py .venv
$ . .venv/bin/activate
(.venv)$ pip install pyramid
I pyvenv
I pip
Hello Pyramid
from pyramid.config import Configurator
from pyramid_view import view_config
@view_config(route_name="hello")
def hello(request):
name = request.matchdict["name"]
body = "Hello {name}".format(name=name)
request.response.text = body
return request.response
def main(global_conf, **settings):
config = Configurator(settings=settings)
config.add_route("hello", "/hello/{name}")
config.scan(".")
return config.make_wsgi_app()
Hello Pyramid
I config.add_route
I Web アプリケーションで扱うURL パターンを登録する
I config.scan
I view_config などのvenusian デコレータのコールバックを実
行する
I view_config
I scan のタイミングでview を登録する
I request_method やroute_name などの条件を指定
TODOLIST アプリケーションその1
I モデル
I Task
I name
I 機能
I タスク一覧
I Task をすべて表示する
I タスク完了のボタンを表示する
I タスク追加のフォームを表示する
I タスク追加
I Task を作成して、タスク一覧に戻る
I タスク終了(削除)
I Task を削除して、タスク一覧に戻る
SQLAlchemy を使う
pip install pyramid_tm pyramid_sqlalchemy
config.include("pyramid_tm")
config.include("pyramid_sqlalchemy")
I pyramid_tm
I transaction を利用した包括的なトランザクション管理
I エラー発生時はロールバック
I pyramid_sqlalchemy
I zope.sqlalchemy
I sqlalchemy.url
ビュー:タスク作成
@view_config(route_name="top",
request_method="POST")
def create_task(request):
name = request.params["name"]
task = Task(name=name)
DBSession.add(task)
location = request.route_url("top")
return HTTPFound(location)
I request.params
I request.route_url
I HTTPFound
ビュー:タスク削除
@view_config(route_name="task_finish",
request_method="POST",
renderer="templates/index.mako")
def delete_task(request):
task_id = request.matchdict["task_id"]
task = Task.query.filter(Task.id == task_id).first()
if task is None:
raise HTTPNotFound
location = request.route_url("top")
return HTTPFound(location)
I request.matchdict
Pyramid で使えるテンプレート
I Pyramid1.0 から1.4 までmako, chameleon サポートが
Pyramid 自体に含まれていた
I Pyramid1.5 以降で標準サポートはstring, json のみ
I 標準サポート
I json
I string
I ライブラリでサポート
I pyramid_mako
I pyramid_chameleon
I pyramid_jinja2
I など
pyramid_mako
pip install pyramid_mako
config.include("pyramid_mako")
I pyramid_mako
I mako テンプレートを使ったrenderer
I include すると“.mako” 拡張子で指定したレンダラーでmako
テンプレートを利用できる
TODOLIST アプリケーションその2
I Route とTraversal とView を活用する
I アプリケーションのロジックはリソースで行うようにする
I CSS などを適用
I deform でフォームを作る
I Task をまとめるTodoList モデル
I ビューであれこれやらないようにする
Pyramid のView が呼ばれるまで
I パターンマッチによりroute を決定
I route に設定されたfactory でresource を作成
I matchdict にtraverse がある場合はresource をトラバース
I 残りのURL を消費しきる
I リソースが__getitem__を持っていない
I 残りのURL が@@ で始まる(ビュー名)
I トラバース結果がcontext となる
I route, context, request method などの条件からview を決定
I view を呼び出す
route とresource factory
config.add_route(
"task",
"/todolists/{todolist_id}/tasks/{task_id}/*traverse",
factory=".resources.task_factory")
I factory
ビューが呼び出されるまで
I URL: /todolist/1/tasks/2/@@finish
I Route: tasks にマッチ
I route_name: tasks
I todolist_id: 1
I task_id: 2
I travarse: @@finish
I tasks route のfactory であるtask_factory が呼び出される
I TaskResource インスタンスがリソースとして作成される
I URL の残りが@@finish となりトラバーサル終了
I ビュー名finish
I task_finish が呼び出される
Deform/Colander でフォーム作成
I Colander
I スキーマ、バリデーションライブラリ
I Deform
I フォームライブラリ
I Peppercorn
I HTML フォームで構造化したデータを扱うためのパーサー
Deform/Colander/peppercorn の動作
I appstruct, pstruct, cstruct
I アプリケーションモデルなどをappstruct にして渡す
I deform がappstruct をフォームウィジェットとともにHTML
にする
I ブラウザからsubmit されるとパラメータはpstruct で渡され
てくる
I peppercoron でpstruct をcstruct に変換
I colander でcstruct をappstruct に変換
Colander スキーマ
import colander as c
import deform.widget as w
class TodolistSchema(c.Schema):
name = c.SchemaNode(c.String())
description = c.SchemaNode(c.String(),
widget=w.RichTextWidget())
I Schema クラス
I SchemaNode
I widget
I deform へのヒントとしてウィジェットを設定する
I 抽象的なスキーマ情報にこういう詳細が入るのはちょっとや
だ(´・ω・`)
pyramid_deform
@view_config(....)
class TodolistForm(FormView):
schema = TodolistSchema()
buttons = ('save',)
@property
def context(self):
return self.request.context
def save_success(self, values):
todolist = self.context.add_todolist(**values)
return HTTPFound(todolist.url)
I FormView を継承してビューを実装する
I schema でcolander スキーマを指定
I buttons でボタンの名前(‘save’) を設定しておくと、対応する
メソッド(save_success) がバリデーション後に呼び出される
appstruct
class TodolistResource(object):
...
def appstruct(self):
return dict(name=self.todolist.name,
description=self.todolist.description)
I ビューのメソッドで詳細に実装したくない
I context に委譲
I FormView はなぜかcontext を持ってないのでrequest 経由で
取得
I appstruct
I deform はフォームの値をdict(appstruct) で受け取る
I appstruct はdeform によってpeppercorn が解釈可能なパラ
メータ(pstruct) をsubmit するフォームとなるようにレンダ
リングされる
pyramid_deform のAPI はあまりきれいじゃない
I 継承ベース
I あまり大きく動作を変えられない
I メソッドオーバーライドによる穴埋め
I フォームバリデーション以外のデータを扱えない
I たとえばDB アクセスして重複チェックなどした場合のエ
ラーとかきれいに表示できない
I あまり多くを望まないように
I 単純なマスタデータ入力以上はできないと思ったほうがよい
deform の扱い
form = deform.Form(TodolistSchema(), buttons=('save',))
controls = request.params.items()
try:
params = form.deserialize(controls)
except ValidationFailure as e:
return dict(form=e)
I request.params.items()
I peppercorn はパラメータの順番が重要
I ValidationFailure
I 入力チェックの例外オブジェクト
I エラー情報を含んだフォームをレンダリングする
static_view
I CSS, JS などを取り扱うにはstatic_view を使う
add_static_view("static", "my.todolist:static")
I “static” はURL で使う名前
I “my.todolist:static” はファイルパスここではasset
specification 記法を使っている
I add_static_view で登録したasset はテンプレートなどで
static_url でURL を利用する
I static_url の例
<link rel="stylesheet"
href="${request.static_url(
'deform:static/css/bootstramp.min.css')}">
<script
src="${request.static_url(
'my.todolist:static/js/app.js')}"></script>
Asset Specification
I “{package}:{directory}” のような文字列でパッケージ以下
のディレクトリを表す
I my.todolist:static は
os.path.join(os.path.dirname(my.todolist.__file__), “static”)
で取得できるディレクトリ
Mako テンプレート
I block タグ
I あとから継承先のテンプレートで埋める場所
I next.body()
I 直接継承しているテンプレートの内容をレンダリングする
I レイアウトなど多段に継承するときに必要
I inherit タグ
I 継承テンプレートを指定
I %for, %if, %while など
I Python の各種制御構文と同じ
I インデントブロックじゃないので、%endfor などが必要
pyramid のセキュリティ機構
I authentication_policy
I アクセスしているユーザーが誰なのか? を判定する方法
I authorization_policy
I アクセスするユーザーは何ができるのか? を判定する方法
permission
@view_config(route_name="top",
permission="todolist.view",
renderer="templates/index.mako")
def index(context, request):
return dict(todolists=context)
I view ごとにpermission を決める
I authorization policy で与えられたpermission がview の
permission を含んでいれば、そのview を利用可能
I 適切なpermission を得られなかった場合はforbidden_view が
呼び出される
ACLAuthorizationPolicy
class TodoListResource(object):
...
def __acl__(self):
return [(Allow, self.todolist.user.username,
'todolist.view'),
(Allow, self.todolist.user.username,
'todolist.edit'),
(Allow, self.todolist.user.username,
'task.create'),
]
I context の__acl__ プロパティでpermission を決定する
I __acl__は、pyramid1.5 以降はメソッドでもよい
I この場合はtodolist の持ち主ならtodolist.view などの
permission を与えられる
ログインビュー
@view_config(route_name="login",
renderer="templates/login.mako")
class LoginView(FormView):
schema = LoginSchema()
buttons = ('login',)
def login_success(self, values):
user = authenticate(self.request,
values["username"],
values["password"])
if not user:
return
headers = security.remember(
self.request, user.username)
res = HTTPFound(self.request.route_url("top"),
headers=headers)
return res
pyramid.security API
I security.remember
I authentication_policy に対して、identity(ログインユーザーな
ど) を記録する(ログイン)
I security.forget
I authentication_policy に対して、identity を消去する(ログア
ウト)
User の判定
def authenticate(request, username, password):
user = User.query.filter(
User.username == username).first()
if not user:
return
if not user.validate_password(password):
return
return user
pyramid のjson 対応
I request.json_body
I リクエストから直接json パースしたオブジェクトを受け取
れる
I json レンダラー
I ビューが返すdict をjson にダンプしてくれる
I オブジェクトが__json__ メソッドを持っているとダンプ中
に呼び出される
I xhr プリディケーション
I view_config でxhr=True としておくと、jquery などのajax リ
クエストはそちらのビューが優先して呼び出される
結局リソースとは
I ビューとモデルの緩衝材
I アプリケーションモデル
I appstruct, __json__, __acl__ などフレームワークへのイン
ターフェイスを提供する
I アダプターパターン
I すべてをリソースでやろうとしないように
I なんでもありになりがち
I コンストラクタで受け取った以上のオブジェクトを扱わない
こと
まとめ
I Pyramid はとても簡単
I 強力なライブラリを活用できる
I JSON アプリケーションのバックエンドとしても優秀
I SQLAlchemy は、がんばって勉強してください
参考文献
I Defending Pyramid’s Design
I http://docs.pylonsproject.org/projects/pyra-mid/
en/1.5-branch/designdefense.html
I Pyramid Documentation
I http://docs.pylonsproject.org/en/latest/docs/pyramid.html
I Deform
I http://deform.readthedocs.org/en/latest/
I Colander
I http://colander.readthedocs.org/en/latest
I pyramid_sqlalchemy
I http://pyramid-sqlalchemy.readthedocs.org/en/latest/
I pyramid_tm
I http://pyramid-tm.readthedocs.org/en/latest/