2012年10月13日土曜日

web2py SQLFORM あれこれ ④ SQLFORM.grid とカスタマイズ1

SQLFORM あれこれ ③ の続きです。今回は SQLFORM.grid を使ったカスタマイズを説明します。

SQLFORM.gridSQLFORM.smartgrid は、ほぼ同じ機能です。ただ SQLFORM.smartgrid の方が、関連するテーブルの参照やレコードメンテナンスが可能で、少し機能が高度になっています。しかし今回は、SQLFORM.grid を説明します。

SQLFORM.grid

SQLFORM あれこれ ② の冒頭で紹介したモデル定義を利用して、試しに次のように SQLFORM.grid を使ったコードを書いてみる。

1
2
3
4
@auth.requires_login()
def manage():
    grid = SQLFORM.grid(db.image, upload=URL(c='default', f='download'))
    return dict(grid=grid)

少し説明を行うと、SQLFORM.gridSQLFORM クラスの スタティックメソッド になる。

第一パラメータとして、Queryオブジェクト を指定する必要がある。ここでは、Tableオブジェクト を指定している。これはショートカット機能によって、db.image は db.image.id > 0 に、Tableオブジェクトから Queryオブジェクトに変換される。

upload パラメータは、SQLFORM あれこれ ① で説明した upload パラメータと同じ機能である。

このコードを実行してみる。

画面イメージのように一覧表示になる。SQLFORM.grid では、次の機能を提供する。

  • 一覧表示
  • 検索
  • ソート
  • ページネーション(ページ割り)
  • 詳細表示
  • 編集
  • 削除
  • 新規作成
  • CSVなどへのエクスポート

詳細表示とデータ編集画面イメージは次のようになる。

詳細表示画面変更画面

SQLFORM.grid を利用すれば、それだけでデータ一覧表示やメンテナンス等に活用できる、便利な機能だ。ただ、いろいろ気に入らない部分もあるので、カスタマイズを試してみる。

SQLFORM.grid のカスタマイズ(アクセス制御)

前のセクションで紹介したコードの一行目で、 @auth.requires_login() と指定している。これはログイン認証を要求するデコレータだが、SQLFORM.grid はログインしていないと編集機能など一部機能を制限するため記述している。この制限は、user_signature パラメータを False で指定すれば解除可能だ。しかしセキュリティ上の問題になるため、通常は解除しない。

この他、アクセス制御に関しては細かな設定が可能だ。詳細参照・編集・削除・新規作成 の各機能に対して、次のアクセス制御のパラメータが用意されている。

パラメータ制御する機能
details詳細参照
editable編集
deletable削除
create新規作成

これらのパラメータに対しては、True/False といったブール値を直接指定しても良いが、特定のグループに属しているか判定するために Auth.has_membershipAuth.has_permission メソッドを指定しても良い。さらに無名関数(lambda)を指定することも可能だ。サンプルを示してみる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@auth.requires_login()
def manage():
    def add_permission(form):
        auth.add_permission(0, 'update', 'image', form.vars.id)
        auth.add_permission(0, 'delete', 'image', form.vars.id)
        
    grid = SQLFORM.grid(db.image, upload=URL(c='default', f='download'),
                details=True,
                editable=lambda r : auth.has_permission('update','image', r)\
                                            or auth.has_membership('admin'), 
                deletable=lambda r : auth.has_permission('delete', 'image', r)\
                                            or auth.has_membership('admin'),
                create=True,
                oncreate=add_permission)
                
    return dict(grid=grid)

サンプルコードでは、次のように各パラメータを指定している。

パラメータ設定値
details
True
editable
lambda r : auth.has_permission('update','image', r)\
             or auth.has_membership('admin')
deletable
lambda r : auth.has_permission('delete', 'image', r)\
             or auth.has_membership('admin')
create
True

この設定では、次のように権限を与えている。

  • 詳細参照は、ログインユーザなら誰でも可能。
  • 編集は、対象レコードへの update パーミッションを持っているか、admin グループに属するユーザ。
  • 削除は、対象レコードへの delete パーミッションを持っているか、admin グループに属するユーザ。
  • 新規作成は、ログインユーザなら誰でも可能。

ユーザに対するパーミッションの付与は、14行目の onreate パラメータに add_permission 関数を割り当てることで実現している。oncreate は指定した関数を、レコード生成時に自動実行する。

このコードを実行してみる。

admin グループに属していないユーザ で、アクセスした場合)

admin グループに属しているユーザ で、アクセスした場合)

admin グループに属しているユーザは、全てのレコードの編集・削除が可能だ。admin グループの属していないユーザは、ユーザが作成したレコードしか編集・削除できないのがわかる。

このコードのままでも問題なく動作するのだが、レコード削除時に削除したレコードのパーミッションのデータが残ってしまうという問題がある。このため、次のようにコードを変更する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@auth.requires_login()
def manage():
    def add_permission(form):
        auth.add_permission(0, 'update', 'image', form.vars.id)
        auth.add_permission(0, 'delete', 'image', form.vars.id)
        
    def del_permission(table, id):
        auth.del_permission(auth.user_group(), 'update', table, id)
        auth.del_permission(auth.user_group(), 'delete', table, id)
        
    def upd_permission(form):
        if form.vars.get(form.FIELDNAME_REQUEST_DELETE, False):
            del_permission('image', form.vars.id)

    grid = SQLFORM.grid(db.image, upload=URL(c='default', f='download'),
                details=True,
                editable=lambda r : auth.has_permission('update','image', r)\
                                            or auth.has_membership('admin'), 
                deletable=lambda r : auth.has_permission('delete', 'image', r)\
                                            or auth.has_membership('admin'),
                create=True,
                oncreate=add_permission, 
                ondelete=del_permission, onupdate=upd_permission)

return dict(grid=grid)

コードを解説すると、del_permission(7行目)と upd_permission(11行目) という2つの関数を追加している。これを、ondelete 及び onupdate パラメータに割り当てている(23行目)。ondelete はレコード削除時に実行する関数を、onupdate はレコード更新時に実行する関数を指定する。レコード編集画面で削除を許可している場合、ondelete ではなく onupdate で指定した関数が自動実行されるため、両方指定することが必要だ。

これによって削除レコードのパーミッションデータを削除することができる。


記事が長くなったので、次回 に続きます。次回も、SQLFOM.grid カスタマイズを更に説明します。なお次のサイトも参考にしてください。

参考
web2py Book - SQLFORM.gridとSQLFORM.smartgrid
web2py 補足ドキュメント - アクセス制御 - Access control