Advertisement

Pyramid入門

open collector
Sep. 14, 2014
Advertisement

More Related Content

Advertisement

Pyramid入門

  1. Pyramid 入門 aodag September 15, 2014
  2. お前誰よ I aodag I Atsushi ODAGiri I 所属 I ビープラウド I Pylonsproject.jp
  3. Pyramid I 特徴 I Web アプリケーションフレームワーク I ドキュメント、テストがしっかりしてるコミュニティ I 長所 I 押しつけがない I フレームワーク自体が拡張可能 I コンポーネントで整理された実装とAPI I 短所 I 押し付けがない I フルスタックでないので各種ライブラリの知識が必要 I 柔軟性の高さは諸刃の剣でもある
  4. 使う理由 I SQLAlchemy と一緒に I 認証方法や権限設定などがアプリケーションに絡んで複雑 I 保存先がDB でない場合などフルスタックがいらない I アレがきらいだから
  5. 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
  6. インストール $ pyvenv.py .venv $ . .venv/bin/activate (.venv)$ pip install pyramid I pyvenv I pip
  7. 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()
  8. 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 などの条件を指定
  9. TODOLIST アプリケーションその1 I モデル I Task I name I 機能 I タスク一覧 I Task をすべて表示する I タスク完了のボタンを表示する I タスク追加のフォームを表示する I タスク追加 I Task を作成して、タスク一覧に戻る I タスク終了(削除) I Task を削除して、タスク一覧に戻る
  10. 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
  11. Route の登録 I add_route # タスク一覧、タスク作成 config.add_route("top", "/") # タスク削除 config.add_route( "task_finish", "/tasks/{task_id}/finish")
  12. ビュー:タスク一覧 @view_config(route_name="top", request_method="GET", renderer="templates/index.mako") def index(request): tasks = Task.query.all() return dict(tasks=tasks) I route_name I request_method I renderer
  13. ビュー:タスク作成 @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
  14. ビュー:タスク削除 @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
  15. 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 など
  16. pyramid_mako pip install pyramid_mako config.include("pyramid_mako") I pyramid_mako I mako テンプレートを使ったrenderer I include すると“.mako” 拡張子で指定したレンダラーでmako テンプレートを利用できる
  17. テンプレート:タスク一覧 <ul> %for task in tasks: <li> ${task.name} <form action="${request.route_url('task_finish', task_id=task.id)}" method="post"> <button type="submit">Finish</button> </form> </li> %endfor </ul>
  18. テンプレート:タスク作成フォーム <form action="${request.route_url('top')}" method="post"> <input type="text" name="name"> <button type="submit">Add</button> </form>
  19. TODOLIST アプリケーションその2
  20. TODOLIST アプリケーションその2 I Route とTraversal とView を活用する I アプリケーションのロジックはリソースで行うようにする I CSS などを適用 I deform でフォームを作る I Task をまとめるTodoList モデル I ビューであれこれやらないようにする
  21. 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 を呼び出す
  22. route とresource factory config.add_route( "task", "/todolists/{todolist_id}/tasks/{task_id}/*traverse", factory=".resources.task_factory") I factory
  23. resource factory def task_factory(request): todolist_id = request.matchdict["todolist_id"] task_id = request.matchdict["task_id"] task = Task.query.filter( Task.id == task_id, Task.todolist_id == todolist_id).first() if taks is None: raise HTTPNotFound return TaskResource(task, request)
  24. アダプター class TaskResource(object): def __init__(self, task, request): self.task = task self.request = request def finish(self): self.task.finish() @property def todolist_url(self): return self.request.route_url( "todolist", todolist_id=self.task.todolist_id) I task ラップする対象 I request API 呼び出しのために必要
  25. view @view_config( route_name="task", name="finish", context=".reources.TaskResource", request_method="POST") def task_finish(context, request): context.finish() return HTTPFound(location=context.todolist_url) I view はコンテキストにイベントを伝える(メソッドを呼び出 す)だけ
  26. ビューが呼び出されるまで 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 が呼び出される
  27. Deform/Colander でフォーム作成 I Colander I スキーマ、バリデーションライブラリ I Deform I フォームライブラリ I Peppercorn I HTML フォームで構造化したデータを扱うためのパーサー
  28. Deform/Colander/peppercorn の動作 I appstruct, pstruct, cstruct I アプリケーションモデルなどをappstruct にして渡す I deform がappstruct をフォームウィジェットとともにHTML にする I ブラウザからsubmit されるとパラメータはpstruct で渡され てくる I peppercoron でpstruct をcstruct に変換 I colander でcstruct をappstruct に変換
  29. 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 抽象的なスキーマ情報にこういう詳細が入るのはちょっとや だ(´・ω・`)
  30. 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) がバリデーション後に呼び出される
  31. フォームのデフォルト値 class EditTodolistForm(FormView): schema = TodolistSchema() buttons = ('save',) @property def context(self): return self.request.context def apptsruct(self): return self.context.appstruct() def save_success(self, values): todolist = self.context todolist.update(**values) return HTTPFound(todolist.url)
  32. 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 するフォームとなるようにレンダ リングされる
  33. pyramid_deform のAPI はあまりきれいじゃない I 継承ベース I あまり大きく動作を変えられない I メソッドオーバーライドによる穴埋め I フォームバリデーション以外のデータを扱えない I たとえばDB アクセスして重複チェックなどした場合のエ ラーとかきれいに表示できない I あまり多くを望まないように I 単純なマスタデータ入力以上はできないと思ったほうがよい
  34. 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 エラー情報を含んだフォームをレンダリングする
  35. 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>
  36. Asset Specification I “{package}:{directory}” のような文字列でパッケージ以下 のディレクトリを表す I my.todolist:static は os.path.join(os.path.dirname(my.todolist.__file__), “static”) で取得できるディレクトリ
  37. ベーステンプレート <html> <head> <link rel="stylesheet" href="${request.static_url( 'deform:static/css/bootstramp.min.css')}"> <%block name="extra_header"></%block> </head> <body> <div class="container"> ${next.body()} </div> </body> </html>
  38. テンプレート <%inherit file="base.mako"> <%block name="extra_header> %for reqt in css_links: <link rel="stylesheet" href="${request.static_url(reqt)}" type="text/css" /> %endfor ... </%block>
  39. Mako テンプレート I block タグ I あとから継承先のテンプレートで埋める場所 I next.body() I 直接継承しているテンプレートの内容をレンダリングする I レイアウトなど多段に継承するときに必要 I inherit タグ I 継承テンプレートを指定 I %for, %if, %while など I Python の各種制御構文と同じ I インデントブロックじゃないので、%endfor などが必要
  40. TODOLIST アプリケーションその3 I User モデルを追加 I 認証、権限を追加
  41. security def includeme(config): secret = config.registry.settings['session.secret'] session_factory = SignedCookieSessionFactory( secret=secret) config.set_session_factory(session_factory) authentication_policy = SessionAuthenticationPolicy() authorization_policy = ACLAuthorizationPolicy() config.set_authentication_policy( authentication_policy) config.set_authorization_policy( authorization_policy) config.set_forbidden_view(forbidden_view)
  42. pyramid のセキュリティ機構 I authentication_policy I アクセスしているユーザーが誰なのか? を判定する方法 I authorization_policy I アクセスするユーザーは何ができるのか? を判定する方法
  43. 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 が 呼び出される
  44. 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 を与えられる
  45. ログインビュー @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
  46. pyramid.security API I security.remember I authentication_policy に対して、identity(ログインユーザーな ど) を記録する(ログイン) I security.forget I authentication_policy に対して、identity を消去する(ログア ウト)
  47. 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
  48. TODOLIST アプリケーションその4 I シングルページアプリケーションにする I リクエスト、レスポンスでJSON を取り扱う
  49. pyramid のjson 対応 I request.json_body I リクエストから直接json パースしたオブジェクトを受け取 れる I json レンダラー I ビューが返すdict をjson にダンプしてくれる I オブジェクトが__json__ メソッドを持っているとダンプ中 に呼び出される I xhr プリディケーション I view_config でxhr=True としておくと、jquery などのajax リ クエストはそちらのビューが優先して呼び出される
  50. リソースに__json__ メソッドを追加する class TodoListResource(object): ... def __json__(self, request): create_task_url = self.request.route_url( "tasks", todolist_id=self.id) return dict( tasks=self.unfinished_tasks, description=self.description, create_task_url=create_task_url)
  51. colander でバリデーション @view_config(route_name="tasks", permission="task.create", renderer="json", xhr=True) def create_task(context, request): schema = TaskSchema() values = request.json_body values = schema.deserialize(values) task = context.add_task(name=values["name"]) return dict(task=task)
  52. 結局リソースとは I ビューとモデルの緩衝材 I アプリケーションモデル I appstruct, __json__, __acl__ などフレームワークへのイン ターフェイスを提供する I アダプターパターン I すべてをリソースでやろうとしないように I なんでもありになりがち I コンストラクタで受け取った以上のオブジェクトを扱わない こと
  53. まとめ I Pyramid はとても簡単 I 強力なライブラリを活用できる I JSON アプリケーションのバックエンドとしても優秀 I SQLAlchemy は、がんばって勉強してください
  54. 参考文献 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/
Advertisement