Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 
...
Upcoming SlideShare
Loading in …5
×

Pyramid入門

10,526 views

Published on

  • 7ページ目のpyramid.view が間違ってますね。
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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/

×