56
Various Web Form Widget Toolkits aodag Pycon JP 2011

Form libraries

Embed Size (px)

DESCRIPTION

Pycon JP 2011 8/27

Citation preview

Page 1: Form libraries

Various Web Form Widget Toolkits

aodagPycon JP 2011

Page 2: Form libraries

お前誰よ

@aodag小田切篤

BeProud勤務今別の部屋で発表しているianと同僚

Djangoきらいです(´・ω・`) PylonsとかPyramidとか、既存のコンポーネント組み合わせてるもののほうが好きです。

Page 3: Form libraries

SQLAlchemy

● データマッパー● すごく柔軟

Page 4: Form libraries

WSGI (PEP-333, PEP-3333)

PythonのWebアプリケーション標準

def hello(environ, start_response): start_response("200 OK", [('Content-type', 'text/plain')]) return ["Hello, world!"]

Page 5: Form libraries

Adminアプリケーション

Djangoのadmin(だけ)はいいね!

SQLAlchemyにも同じようなものがほしい

WSGIアプリで全般的に使いたい(あまりフレームワークに依存したくない)

Ajaxばりばりである必要はないけど、DatePickerとかSuggestとか、入力補助系のJSが利用できるとよい

Page 6: Form libraries

Adminアプリケーション

● クラスごとにサブアプリケーション● グリッド表示と検索● 入力フォーム● カスタムアクションを追加できる

Page 7: Form libraries

フォームライブラリを調査

Form Libraryの役割比較ライブラリ

Page 8: Form libraries

Form Libraryの役割

● HTMLフォーム生成● バリデーション

Page 9: Form libraries

比較ライブラリ

● ToscaWidgets / Sprox● FormAlchemy● tw2.sqla● WTForms● deform/colander

Page 10: Form libraries

比較のポイント

● SQLAlchemyとの親和性● バリデータのカスタマイズ● フィールドのカスタマイズ● ウィジェットのカスタマイズ

Page 11: Form libraries

ToscaWidgets / Sprox

● SproxはSQLAlchemyのスキーマからToscaWidgetsのフォームを作成します

● SQLAlchemyのスキーマ以外のフィールドをフォームに追加したり、スキーマのフィールドをフォームから削除したりできます。

● formencodeでカスタムバリデータを作成します● tw.formsでカスタムフィールドを作成します

Page 12: Form libraries

FormAlchemy

● SQLAlchemyだけでなく、zope.schemaなどにも対応● SQLAlchemyのスキーマ対応は一番すぐれている● デフォルトで用意されているウィジェットレンダラーが少ない● 実行時にもフィールド定義を変更可能● jqueryuiを使ったfa.jqueryのような追加スキンが存在する

Page 13: Form libraries

tw2

● ToscaWidgetsの後継● まだ a4がリリースされたばかり、 発展途上● SQLAlchemyから自動生成するフォームがある

○ many-to-manyまでは対応できてない● SQLAlchemyというよりElixirに対応している

Page 14: Form libraries

wtforms

● SQLAlchemyからのスキーマ生成はしない● SelectのoptionをSQLAlchemyのクエリで設定可能● 機能は少なめ● その分はまりどころが少なく枯れるのが早そう● フォーム全体の生成はしない● グリッド生成もしない● ToscaWidgetsのtw.formsと名前が紛らわしい><

Page 15: Form libraries

deform / colander

● colanderはスキーマ定義● deformはcolanderに対応しているフォームライブラリ● SQLAlchemyからのスキーマ生成できない● ウィジェットが豊富● ajaxとりこみに意欲的● deferred bindingにより実行時にウィジェットやバリデーションを変

更可能● テンプレートにchameleon(zope page template)を使っている● テンプレートをmakoに入れ替えるプロジェクトが進行中

http://deformdemo.repoze.org/

Page 16: Form libraries

Sample Model

SQLAlchemy● User

○ user_name○ password○ user_image○ groups

● Group○ name○ users○ permissions

● Permission○ name○ groups

Page 17: Form libraries
Page 18: Form libraries

ポイント

● 全部many-to-manyの関連付け● User - Group - Permission● User - Permissionの派生関連付け● _password 直接見せたくないフィールド● ユーザー画像はファイル保存

Page 19: Form libraries

Userフォーム Sprox

class UserForm(AddRecordForm): __model__ = models.User __require_fields__ = ['user_name'] __omit_fields__ = ['_password'] __field_order__ = ['user_name', 'password', 'groups']

password = tw.forms.PasswordField('password', validator=tw.forms.validators.NotEmpty)

Page 20: Form libraries
Page 21: Form libraries

Userグリッド Sprox

class UserTable(TableBase): __model__ = models.User

user_table = UserTable(models.DBSession)

class UserTableFiller(TableFiller): __model__ = models.User

user_table_filler = UserTableFiller(models.DBSession)

user_table(user_table_filler.get_value())

Page 22: Form libraries
Page 23: Form libraries

Sprox雑感

● AddRecordFormとEditableFormをそれぞれ作らないといけない● SQLAlchemy0.7で動かない!● many-to-manyがうまくフォームに反映されない● デフォルトでアルファベット順になってしまうので、いい感じの順

番にするには、全部指定しなおさないといけません(´・ω・`) 

Page 24: Form libraries

(´・ω・`) そろそろ

オワコン?

Page 25: Form libraries

Userフォーム FormAlchemy

class UserForm(FieldSet): def __init__(self, **kw): super(UserForm, self).__init__(model=User, **kw)

excludes = [self._password] # 追加フィールド self.insert_after(self.user_name, Field('password').password().required()) self.configure(exclude=excludes) # フォーム全体の設定

Page 26: Form libraries
Page 27: Form libraries

User グリッド FormAlchemy

class UserGrid(Grid): def __init__(self, **kw): super(UserGrid, self).__init__(cls=models.User, **kw)

# Edit用のリンク追加 self.append(Field('edit_link', value=lambda u: '<a href="%s/edit">Edit</a>' % u.id)) self.configure(readonly=True, exclude=[self._password])

users = models.DBSession.query(models.User).all()grid = user_grid.bind(users)

Page 28: Form libraries
Page 29: Form libraries

FormAlchemy雑感

やっぱりSQLAlchemy0.7で動かない(´・ω・`) fa.jqueryはまだ安定していないmany-to-manyをしっかりおいかけてくれる開発が活発なので、今後に期待できる

Page 30: Form libraries

全体的には(・∀・)イイ!

と思う

Page 31: Form libraries

Userフォーム tw2.sqla

class UserForm(tw2.sqla.DbFormPage): entity = models.User class child(tw2.forms.TableForm): user_name = tw2.forms.TextField(validator=tw2.core.Required) password = tw2.forms.PasswordField(validator=tw2.core.Required) user_image = tw2.forms.FileField() groups = tw2.sqla.DbSingleSelectField(entity=models.Group)

Page 32: Form libraries

● SQLAlchemyのスキーマから自動生成する機能が追加されてきていますが、使い物になりませんでした。(´・ω・`) 

● あと、entityクラスのqueryメソッドを呼ぼうとしたり、Elixirを前提にしすぎです。

● 遅延評価できるselectウィジェットに複数選択可能なものがなく、many-to-manyの関連付けに困ります

Page 33: Form libraries

(゚д゚)マダマダ

Page 34: Form libraries

Userフォーム WTForms

def group_factory(): return models.DBSession.query(models.Group)

class UserForm(wtforms.Form): username = wtforms.TextField('User Name') password = wtforms.PasswordField('Password') groups = QuerySelectMultipleField(query_factory=group_factory )

Page 35: Form libraries

WTForms表示

<form method="post"><table>${self.field_row(form.username)}${self.field_row(form.password)}${self.field_row(form.groups)}</table><button type="submit">Add</button></form>

Page 36: Form libraries

WTForms 表示

<%def name="field_row(field)"><tr><td>${field.label}</td><td>${field()}</td></tr></%def>

Page 37: Form libraries
Page 38: Form libraries

WTForms 雑感

● やれることが少ない分、はまりどころはなさそうです● でもフォームライブラリ使ってるのにHTMLテーブル書くのはやで

す。● B2Cサイトで複雑なHTMLに入れるのに向いてそうですが、そん

なことは他のフォームライブラリでできます

Page 39: Form libraries

機能少なすぎね?

(´・ω・`) 

Page 40: Form libraries

Userフォーム deform

class UserSchema(c.MappingSchema): # colanderはSQLAlchemyから自動生成しない user_name = c.SchemaNode(c.String()) password = c.SchemaNode(c.String(), widget=w.PasswordWidget())

form = Form(UserSchema(), buttons=('save',))

Page 41: Form libraries
Page 42: Form libraries

colanderのdeferred bind 定義

実行時に、ウィジェット、やバリデータを切り替える仕組み

@c.deferreddef group_select_widget(node, kw): groups = kw['groups'] return w.SelectWidget(values=[ (g.id, g.group_name) for g in groups ])

Page 43: Form libraries

colander deffered binding

class Group(c.MappingSchema): # groupを選択するためのスキーマ group_id = c.SchemaNode(c.String(), widget=group_select_widget)

class Groups(c.SequenceSchema): # groupを複数選択するためのスキーマ group = Group()

class UserSchema(c.MappingSchema): ...... groups = Groups()

Page 44: Form libraries

colanderのdeferred bind バインディング

schema = UserSchema()

groups = DBSession.query(Group)

# バインドschema = schema.bind(groups=groups)

form = Form(schema, ....)

Page 45: Form libraries
Page 46: Form libraries

User フォーム deform バリデーション

try: params = form.validate(controls)except ValidationFailure, e: e.render()

Page 47: Form libraries

deform 雑感

● フォームでやりたいことは、おそらくなんでもできます。● シーケンススキーマやマッピングスキーマを組み合わせることで、

複雑な階層を持つスキーマも作成可能。● その分ライブラリの構造が複雑です。

Page 48: Form libraries

(´  > ω < )むずかしー!

Page 49: Form libraries

ひとまずSQLAlchemyのデータ管理ツールを作るなら、FormAlchemyが一番サポートされている。fa.jqueryは様子見たほうがいい。

1リクエストで複数のモデルを扱う場合は、colander / deform がほぼどんな構造でも対応できる。MongoDBなどスキーマレスDBを使う場合は、こちらをおすすめする。

Page 50: Form libraries

スタティックファイルの管理

deformやfa.jqueryはjquery.jsやjqueryui.js、その他cssなどが必要

フロントのApacheやnginexに任せてしまいたいが、フォームライブラリが使うスタティックファイルはどこにあるのか?

ウィジェットライブラリが依存するjsなどをどう管理していくか?

Page 51: Form libraries

Paste deployでがんばr

[app:deform_static]use = egg:paste#pkg_resourcesegg = deformresource_name = deform/static

[composite:deform]use = egg:paste#urlmap/ = deform_app/static = deform_static

[pipeline:deform_demo]pipeline = egg:repoze.tm2#tm deform

Page 52: Form libraries

Fanstatic

● スタティックファイルをホスティングするWSGIアプリ● スタティックファイルを管理するユーティリティ、ミドルウェア● fa.jqueryは今後これを使うようになる予定

http://pypi.python.org/pypi?%3Aaction=search&term=fanstatic&submit=search

Page 53: Form libraries

Fanstatic 例

from fanstatic import Fanstaticfrom js.jqueryui import jqueryui

@wsgifydef app(request): jqueryui.need() return Response(body)

app = Fanstatic(app)

Page 54: Form libraries

Fanstatic 例

body = """\<html><head></head><body>Hello</body></html>"""

Page 55: Form libraries

Fanstatic 例 実行結果

<html> <head> <script type="text/javascript" src="/fanstatic/jquery/jquery.js"></script> <script type="text/javascript" src="/fanstatic/jqueryui/ui/jquery-ui.js"></script> </head> <body> Hello</body> </html>

Page 56: Form libraries

参考

● SQLAlchemy http://www.sqlalchemy.org/● ToscaWidgets http://toscawidgets.org/● formencode http://formencode.org/● Sprox http://sprox.org/● tw2 http://toscawidgets.org/documentation/tw2.core/● formalchemy http://docs.formalchemy.org/formalchemy● fa.jquery http://docs.formalchemy.org/fa.jquery/● WTForms http://wtforms.simplecodes.com/● colandar https://docs.pylonsproject.org/projects/colander/dev/● deform https://docs.pylonsproject.org/projects/deform/dev/● fanstatic http://www.fanstatic.org/en/0.11.2/index.html