2012年11月2日金曜日

web2py SQLFORM あれこれ ⑥ SQLTALBLEの利用

SQLFORM.grid とカスタマイズ1SQLFORM.grid とカスタマイズ2 で、SQLFORM.grid を説明しました。SQLFORM.grid はそれだけで、テーブルの一覧表示やレコードの作成・表示・修正・削除が可能な、非常に便利な機能です。しかしカスタマイズという観点からは、できることは限定されます。 このため今回は SQLTABLE を使用し、もう少し柔軟なカスタマイズを試してみます。

SQLTABLE で仮想フィールドを利用する場合の注意点

SQLTABLE と SQLFORMの連携について考えてみるが、SQLTABLE 自体に関しては、こちら を読んで欲しい。今回、SQLTABLE では 仮想フィールド を利用するが、web2pyのバージョンに関する次の注意点がある。

SQLTABLE で仮想フィールドを利用する場合の注意点
  • 仮想フィールドは、web2py ver. 2.0.02.2.1 では利用できません(バグのため)。
  • ニュースタイル仮想フィールド は、 ver.1.x.x を含む、web2py ver.2.2.1 以前では利用できません(未対応だったため)。

これはあくまでも、仮想フィールドを SQLTABLE で利用する 場合の注意点だ。現在の最新は 2.2.1 だが、 2.2.1 を含めて 2.x.x 系では仮想フィールドを利用できない。ニュースタイルは、2.x.x 及び 1.x.x でも利用できない。

記事執筆時点で利用可能なのは、1.99.7 などの 1.x.x 系の過去のバージョンと、ニュースタイルも含めたら、trunk バージョンのみとなる。もし今現在、trunk バージョンを利用している場合、最新に更新して欲しい。

参考:web2py download

SQLTABLE と SQLFORM の連携(仮想フィールド利用)

SQLFORM に関しては、SQLFORM基本 及び SQLFORMカスタマイズ で説明をした。記事のコードでは URLパラメータでレコードIDを渡し、該当するIDのレコードが存在する場合は編集・削除が、存在しない場合は新規作成を行う。今回は SQLFORMカスタマイズ に紹介したコードと共に使用可能な、 SQLTABLE のコードを作成する。

まず、次の SQLTABLE を使ったコードを見て欲しい。

 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
26
27
28
29
30
31
32
33
34
@auth.requires_login()
def table():
    class virtual_image(object):
        def edit_image(self):
            if self.image.url:
                url = self.image.url 
            else:
                url = URL(c='default', f='download', args=self.image.file)
            return A(IMG(_src=url, _width=50, _height=40),
                    _href=URL(f=image, args=self.image.id, 
                    user_signature=True))
            
        def edit_link(self):
            url = URL(f=image, args=self.image.id, user_signature=True)
            return A(I(_class='icon-pencil'),
                     T('Edit'),_class='btn', _href=url)

    rows = db(db.image).select()
    rows.setvirtualfields(image=virtual_image())
    
    columns = ['image.edit_image', 'image.name', 'image.category', 
               'image.edit_link']
    headers = {'image.edit_image':'', 'image.name':T('Name'), 
               'image.category':T('Category'),
               'image.edit_link':{'class':'row_buttons', 'label':'', 
                                'width':'60%', 'selected':False, 
                                'truncate':0}}

    add_link = A(I(_class='icon-plus'), T('Add'), _class='btn', 
                 _href=URL(f=image, user_signature=True))
    table = DIV(add_link, SQLTABLE(rows, columns=columns, 
                            headers=headers, _class='web2py_grid'))

    return dict(table=table)
syntax2html

このコードでは、Virutal_image クラス定義(2行目)で仮想フィールドの定義を行なっている。さらに、setvirtualfields メソッドで、Rowsオブジェクトに対しての仮想フィールドを設定する(18行目)。最後に、これらの仮想フィールドを含めた、SQLTABLE オブジェクトを生成している(29行目)。

また、SQLTABLE で仮想フィールドを表示する場合に、仮想フィールドを columns パラメータに指定することを忘れてはいけない(20・21・29行目)。columnsパラメターには表示するフィールドを指定するのだが、このパラメータに指定した順にフィールドが表示される事にもなる。

コードを実行すると、次の画面が表示される。

画面イメージでは仮想フィールドで定義した、画像及び編集ボタンが追加されている。画像及び編集ボタンは、href属性に image 関数が、URL変数にはレコードのid値が設定されている。このため、クリックすると SQLFORMの編集画面に遷移する。

今回のコードでは、Twitter Bootstrapを利用している。Bootstrapの詳細は、Twitter Bootstrap の利用 を参照して欲しい。また画面は、SQLFORM.grid のデザインに合わせるため、web2py_grid クラスを指定している。もし Bootstrap のテーブルデザインにしたい場合、例えば次のように、web2py_gridの代わりにクラスに指定する。

table table-striped table-bordered

すると画面は次のようなイメージになる。

SQLTABLE と SQLFORM の連携(ニュースタイル仮想フィールド利用)

同様に、ニュースタイル仮想フィールド でコードを書いてみる。

 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
26
27
28
29
30
31
32
@auth.requires_login()
def table():
    def edit_image(row):
        if row.image.url:
            url = row.image.url 
        else:
            url = URL(c='default', f='download', args=row.image.file)
        return A(IMG(_src=url, _width=50, _height=40),
                _href=URL(f=image, args=row.image.id, user_signature=True))
        
    def edit_link(row):
        url = URL(f=image, args=row.image.id, user_signature=True)
        return A(I(_class='icon-pencil'),
                 T('Edit'),_class='btn', _href=url)

    db.image.edit_image = Field.Virtual(lambda row : edit_image(row)) 
    db.image.edit_link = Field.Virtual(lambda row : edit_link(row)) 
    rows = db(db.image).select()
    
    columns = ['image.edit_image', 'image.name', 'image.category', 
               'image.edit_link']
    headers = {'image.edit_image':'', 'image.name':T('Name'), 
               'image.category':T('Category'),
               'image.edit_link':{'class':'row_buttons', 'label':'', 
               'width':'60%', 'selected':False, 'truncate':0}}

    add_link = A(I(_class='icon-plus'), T('Add'), _class='btn', 
                 _href=URL(f=image, user_signature=True))
    table = DIV(add_link, SQLTABLE(rows, columns=columns, 
                                headers=headers, _class='web2py_grid'))

    return dict(table=table)
syntax2html

オールドスタイルの仮想フィールドとほぼ同じコードだが、16・17行目でニュースタイルで設定している。

SQLFORM のコード

新規作成・編集・削除を行う SQLFORM のコードは、SQLFORMカスタマイズTwitter Bootstrap の利用 で紹介したコードで良いのだが、登録に成功した場合と、キャンセルボタンを押した時のリダイレクト先が設定されていなかった。これらを設定すると、次のコードになる。

 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def image():
    def check_image(form):
        file_delete = '%s__delete' % 'file'
        if form.vars.url_file == 'url': 
            if not form.vars.url:
                form.errors.url = T('must enter a url')
            else:
                form.vars[file_delete] = 'on'
        else:
            if form.vars.file is None and \
                (form.vars.has_key(file_delete) is False or 
                 form.vars.get(file_delete, False)):
                form.errors.file = T('must select a file')
            else:
                form.vars.url = ''
            
    onchange_js = "jQuery.noConflict();\
                if(jQuery('#url').attr('checked')){\
                    jQuery('#image_url__row').show();\
                    jQuery('#image_file__row').fadeOut();}\
                else {\
                    jQuery('#image_file__row').show();\
                    jQuery('#image_url__row').fadeOut();}"    
    ready_js = "jQuery(document).ready(function(){" + onchange_js + ";});"

    record = db.image(request.args(0))
    if not record or record.url:
        checked = True 
    else:
        checked = False
    
    label = T('Image') + ':'
    radio = P(INPUT(_type='radio', _name='url_file', _value='url', 
            _checked=checked, _id='url', _onchange=onchange_js), 
            LABEL(T('url'), _for='url'), ' ', 
            INPUT(_type='radio', _name='url_file', _value='file', 
            _checked=not checked, _id='file', _onchange=onchange_js),
            LABEL(T('file'), _for='file'), _style="margin:0;padding:4px 0")
    position = -5 if record else -4
            
    extra_element = TR(TD(LABEL(label),_class="w2p_fl"), 
                    TD(radio, _class="w2p_fw"),
                    TD('', _class="w2p_fc"), _id='url_file__row')

    submit = BUTTON(I(_class='icon-ok icon-white', 
                      _style='margin-right: 5px;'),
                    T('OK'), _type='submit', _class='btn btn-primary')
    cancel = BUTTON(I(_class='icon-remove', _style='margin-right: 5px;'),
                    T('Cancel'), _type='reset', _class='btn',
                     _onclick = 'location.href="%s"' % URL(f='table'),
                     _style='margin-left:10px')

    form = SQLFORM(db.image, record, True, 
                   buttons=[submit,cancel],
                   upload=URL(c='default', f='download'))
    form[0].insert(position, extra_element)
    form = form.process(onvalidation=check_image, next=URL(f='table'))
    form = DIV(form, SCRIPT(ready_js, _type='text/javascript'))
    form.element(_id='image_url')['_placeholder'] = 
                                            'http://www.example.com'
   
    return dict(form=form)
syntax2html

登録が成功した場合のリダイレクト先は、processメソッドの next パラメータにセットする(56行目)。キャンセルボタンを押した場合のリダイレクト先は、BUTTONタグの onclick 属性にセットする(49行目)。

実行した場合の画面イメージは次のようになる。

新規登録編集・削除
注意点など

SQLTABLE と SQLFORM の連携は、SQLFORM.grid を使用する場合に比べて自由度が高い。しかし SQLFORM.grid が持っていた次の機能は、別途コードを記述する必要がある。

  • 検索
  • ソート
  • ページネーション(ページ割り)
  • CSVなどへのエクスポート

また SQLTABLE には、SQLFORM.grid とカスタマイズ2 で説明した SQLFORM.grid の link パラメータによく似た、extracolumns パラメータが存在する。extracolumns は自分で定義したフィールドを SQLTABLE に追加できる機能だが、仮想フィールドを使う場合に比べて自由度が落ちる。このため SQLTABLE を使う場合は、今回の記事のように仮想フィールドを使用した方が何かと便利だと思う。


今回の記事はこれで終わりです。「SQLFORM あれこれ」のシリーズも、ほぼ終わりです。最後に次回付録的に、SQLTABLE に検索機能を付け加える例を紹介してみたいと思います。