Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.
Pyramid 入門 
aodag 
September 15, 2014
お前誰よ 
I aodag 
I Atsushi ODAGiri 
I 所属 
I ビープラウド 
I Pylonsproject.jp
Pyramid 
I 特徴 
I Web アプリケーションフレームワーク 
I ドキュメント、テストがしっかりしてるコミュニティ 
I 長所 
I 押しつけがない 
I フレームワーク自体が拡張可能 
I コンポーネントで整理された実装とAPI...
使う理由 
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...
インストール 
$ 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="hel...
Hello Pyramid 
I config.add_route 
I Web アプリケーションで扱うURL パターンを登録する 
I config.scan 
I view_config などのvenusian デコレータのコールバックを実...
TODOLIST アプリケーションその1 
I モデル 
I Task 
I name 
I 機能 
I タスク一覧 
I Task をすべて表示する 
I タスク完了のボタンを表示する 
I タスク追加のフォームを表示する 
I タスク追加 ...
SQLAlchemy を使う 
pip install pyramid_tm pyramid_sqlalchemy 
config.include("pyramid_tm") 
config.include("pyramid_sqlalchem...
Route の登録 
I add_route 
# タスク一覧、タスク作成 
config.add_route("top", "/") 
# タスク削除 
config.add_route( 
"task_finish", 
"/tasks/{...
ビュー:タスク一覧 
@view_config(route_name="top", 
request_method="GET", 
renderer="templates/index.mako") 
def index(request): 
t...
ビュー:タスク作成 
@view_config(route_name="top", 
request_method="POST") 
def create_task(request): 
name = request.params["name"...
ビュー:タスク削除 
@view_config(route_name="task_finish", 
request_method="POST", 
renderer="templates/index.mako") 
def delete_ta...
Pyramid で使えるテンプレート 
I Pyramid1.0 から1.4 までmako, chameleon サポートが 
Pyramid 自体に含まれていた 
I Pyramid1.5 以降で標準サポートはstring, json のみ ...
pyramid_mako 
pip install pyramid_mako 
config.include("pyramid_mako") 
I pyramid_mako 
I mako テンプレートを使ったrenderer 
I inclu...
テンプレート:タスク一覧 
<ul> 
%for task in tasks: 
<li> 
${task.name} 
<form action="${request.route_url('task_finish', 
task_id=tas...
テンプレート:タスク作成フォーム 
<form action="${request.route_url('top')}" method="post"> 
<input type="text" name="name"> 
<button type...
TODOLIST アプリケーションその2
TODOLIST アプリケーションその2 
I Route とTraversal とView を活用する 
I アプリケーションのロジックはリソースで行うようにする 
I CSS などを適用 
I deform でフォームを作る 
I Task...
Pyramid のView が呼ばれるまで 
I パターンマッチによりroute を決定 
I route に設定されたfactory でresource を作成 
I matchdict にtraverse がある場合はresource をト...
route とresource factory 
config.add_route( 
"task", 
"/todolists/{todolist_id}/tasks/{task_id}/*traverse", 
factory=".reso...
resource factory 
def task_factory(request): 
todolist_id = request.matchdict["todolist_id"] 
task_id = request.matchdict[...
アダプター 
class TaskResource(object): 
def __init__(self, task, request): 
self.task = task 
self.request = request 
def fini...
view 
@view_config( 
route_name="task", 
name="finish", 
context=".reources.TaskResource", 
request_method="POST") 
def ta...
ビューが呼び出されるまで 
I URL: /todolist/1/tasks/2/@@finish 
I Route: tasks にマッチ 
I route_name: tasks 
I todolist_id: 1 
I task_id: ...
Deform/Colander でフォーム作成 
I Colander 
I スキーマ、バリデーションライブラリ 
I Deform 
I フォームライブラリ 
I Peppercorn 
I HTML フォームで構造化したデータを扱うためのパ...
Deform/Colander/peppercorn の動作 
I appstruct, pstruct, cstruct 
I アプリケーションモデルなどをappstruct にして渡す 
I deform がappstruct をフォームウ...
Colander スキーマ 
import colander as c 
import deform.widget as w 
class TodolistSchema(c.Schema): 
name = c.SchemaNode(c.Str...
pyramid_deform 
@view_config(....) 
class TodolistForm(FormView): 
schema = TodolistSchema() 
buttons = ('save',) 
@proper...
フォームのデフォルト値 
class EditTodolistForm(FormView): 
schema = TodolistSchema() 
buttons = ('save',) 
@property 
def context(sel...
appstruct 
class TodolistResource(object): 
... 
def appstruct(self): 
return dict(name=self.todolist.name, 
description=s...
pyramid_deform のAPI はあまりきれいじゃない 
I 継承ベース 
I あまり大きく動作を変えられない 
I メソッドオーバーライドによる穴埋め 
I フォームバリデーション以外のデータを扱えない 
I たとえばDB アクセスし...
deform の扱い 
form = deform.Form(TodolistSchema(), buttons=('save',)) 
controls = request.params.items() 
try: 
params = for...
static_view 
I CSS, JS などを取り扱うにはstatic_view を使う 
add_static_view("static", "my.todolist:static") 
I “static” はURL で使う名前 
I...
Asset Specification 
I “{package}:{directory}” のような文字列でパッケージ以下 
のディレクトリを表す 
I my.todolist:static は 
os.path.join(os.path.d...
ベーステンプレート 
<html> 
<head> 
<link rel="stylesheet" 
href="${request.static_url( 
'deform:static/css/bootstramp.min.css')}">...
テンプレート 
<%inherit file="base.mako"> 
<%block name="extra_header> 
%for reqt in css_links: 
<link rel="stylesheet" 
href="$...
Mako テンプレート 
I block タグ 
I あとから継承先のテンプレートで埋める場所 
I next.body() 
I 直接継承しているテンプレートの内容をレンダリングする 
I レイアウトなど多段に継承するときに必要 
I inh...
TODOLIST アプリケーションその3 
I User モデルを追加 
I 認証、権限を追加
security 
def includeme(config): 
secret = config.registry.settings['session.secret'] 
session_factory = SignedCookieSessi...
pyramid のセキュリティ機構 
I authentication_policy 
I アクセスしているユーザーが誰なのか? を判定する方法 
I authorization_policy 
I アクセスするユーザーは何ができるのか? を判...
permission 
@view_config(route_name="top", 
permission="todolist.view", 
renderer="templates/index.mako") 
def index(conte...
ACLAuthorizationPolicy 
class TodoListResource(object): 
... 
def __acl__(self): 
return [(Allow, self.todolist.user.usern...
ログインビュー 
@view_config(route_name="login", 
renderer="templates/login.mako") 
class LoginView(FormView): 
schema = LoginSch...
pyramid.security API 
I security.remember 
I authentication_policy に対して、identity(ログインユーザーな 
ど) を記録する(ログイン) 
I security.for...
User の判定 
def authenticate(request, username, password): 
user = User.query.filter( 
User.username == username).first() 
i...
TODOLIST アプリケーションその4 
I シングルページアプリケーションにする 
I リクエスト、レスポンスでJSON を取り扱う
pyramid のjson 対応 
I request.json_body 
I リクエストから直接json パースしたオブジェクトを受け取 
れる 
I json レンダラー 
I ビューが返すdict をjson にダンプしてくれる 
I ...
リソースに__json__ メソッドを追加する 
class TodoListResource(object): 
... 
def __json__(self, request): 
create_task_url = self.reques...
colander でバリデーション 
@view_config(route_name="tasks", 
permission="task.create", 
renderer="json", 
xhr=True) 
def create_ta...
結局リソースとは 
I ビューとモデルの緩衝材 
I アプリケーションモデル 
I appstruct, __json__, __acl__ などフレームワークへのイン 
ターフェイスを提供する 
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 
...
Prochain SlideShare
Chargement dans…5
×

Pyramid入門

  • Soyez le premier à commenter

Pyramid入門

  1. 1. Pyramid 入門 aodag September 15, 2014
  2. 2. お前誰よ I aodag I Atsushi ODAGiri I 所属 I ビープラウド I Pylonsproject.jp
  3. 3. Pyramid I 特徴 I Web アプリケーションフレームワーク I ドキュメント、テストがしっかりしてるコミュニティ I 長所 I 押しつけがない I フレームワーク自体が拡張可能 I コンポーネントで整理された実装とAPI I 短所 I 押し付けがない I フルスタックでないので各種ライブラリの知識が必要 I 柔軟性の高さは諸刃の剣でもある
  4. 4. 使う理由 I SQLAlchemy と一緒に I 認証方法や権限設定などがアプリケーションに絡んで複雑 I 保存先がDB でない場合などフルスタックがいらない I アレがきらいだから
  5. 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. 6. インストール $ pyvenv.py .venv $ . .venv/bin/activate (.venv)$ pip install pyramid I pyvenv I pip
  7. 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. 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. 9. TODOLIST アプリケーションその1 I モデル I Task I name I 機能 I タスク一覧 I Task をすべて表示する I タスク完了のボタンを表示する I タスク追加のフォームを表示する I タスク追加 I Task を作成して、タスク一覧に戻る I タスク終了(削除) I Task を削除して、タスク一覧に戻る
  10. 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. 11. Route の登録 I add_route # タスク一覧、タスク作成 config.add_route("top", "/") # タスク削除 config.add_route( "task_finish", "/tasks/{task_id}/finish")
  12. 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. 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. 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. 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. 16. pyramid_mako pip install pyramid_mako config.include("pyramid_mako") I pyramid_mako I mako テンプレートを使ったrenderer I include すると“.mako” 拡張子で指定したレンダラーでmako テンプレートを利用できる
  17. 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. 18. テンプレート:タスク作成フォーム <form action="${request.route_url('top')}" method="post"> <input type="text" name="name"> <button type="submit">Add</button> </form>
  19. 19. TODOLIST アプリケーションその2
  20. 20. TODOLIST アプリケーションその2 I Route とTraversal とView を活用する I アプリケーションのロジックはリソースで行うようにする I CSS などを適用 I deform でフォームを作る I Task をまとめるTodoList モデル I ビューであれこれやらないようにする
  21. 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. 22. route とresource factory config.add_route( "task", "/todolists/{todolist_id}/tasks/{task_id}/*traverse", factory=".resources.task_factory") I factory
  23. 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. 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. 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. 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. 27. Deform/Colander でフォーム作成 I Colander I スキーマ、バリデーションライブラリ I Deform I フォームライブラリ I Peppercorn I HTML フォームで構造化したデータを扱うためのパーサー
  28. 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. 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. 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. 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. 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. 33. pyramid_deform のAPI はあまりきれいじゃない I 継承ベース I あまり大きく動作を変えられない I メソッドオーバーライドによる穴埋め I フォームバリデーション以外のデータを扱えない I たとえばDB アクセスして重複チェックなどした場合のエ ラーとかきれいに表示できない I あまり多くを望まないように I 単純なマスタデータ入力以上はできないと思ったほうがよい
  34. 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. 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. 36. Asset Specification I “{package}:{directory}” のような文字列でパッケージ以下 のディレクトリを表す I my.todolist:static は os.path.join(os.path.dirname(my.todolist.__file__), “static”) で取得できるディレクトリ
  37. 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. 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. 39. Mako テンプレート I block タグ I あとから継承先のテンプレートで埋める場所 I next.body() I 直接継承しているテンプレートの内容をレンダリングする I レイアウトなど多段に継承するときに必要 I inherit タグ I 継承テンプレートを指定 I %for, %if, %while など I Python の各種制御構文と同じ I インデントブロックじゃないので、%endfor などが必要
  40. 40. TODOLIST アプリケーションその3 I User モデルを追加 I 認証、権限を追加
  41. 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. 42. pyramid のセキュリティ機構 I authentication_policy I アクセスしているユーザーが誰なのか? を判定する方法 I authorization_policy I アクセスするユーザーは何ができるのか? を判定する方法
  43. 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. 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. 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. 46. pyramid.security API I security.remember I authentication_policy に対して、identity(ログインユーザーな ど) を記録する(ログイン) I security.forget I authentication_policy に対して、identity を消去する(ログア ウト)
  47. 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. 48. TODOLIST アプリケーションその4 I シングルページアプリケーションにする I リクエスト、レスポンスでJSON を取り扱う
  49. 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. 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. 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. 52. 結局リソースとは I ビューとモデルの緩衝材 I アプリケーションモデル I appstruct, __json__, __acl__ などフレームワークへのイン ターフェイスを提供する I アダプターパターン I すべてをリソースでやろうとしないように I なんでもありになりがち I コンストラクタで受け取った以上のオブジェクトを扱わない こと
  53. 53. まとめ I Pyramid はとても簡単 I 強力なライブラリを活用できる I JSON アプリケーションのバックエンドとしても優秀 I SQLAlchemy は、がんばって勉強してください
  54. 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/

×