2.16. コールバック関数¶
挿入・更新・削除といったレコード操作の前後に、コールバック関数を設定することが可能です。
Note
レコード操作でフォームのみを使用する場合は、コールバック関数を使用するより、 FORMやSQLFORMの onvalidation や onsuccess といったパラメータを設定した方が良いです。 これにより適切なエラー処理を行うことが可能になります。
設定は、 Table クラスの属性にリストとして設定します。次のような属性があります。
属性 説明 _before_insert
レコード 挿入前 に設定された関数を実行します _before_update
レコード 更新前 に設定された関数を実行します _before_delete
レコード 削除前 に設定された関数を実行します _after_insert
レコード 挿入後 に設定された関数を実行します _after_update
レコード 更新後 に設定された関数を実行します _after_delete
レコード 削除後 に設定された関数を実行します
説明では、次の2つのテーブルを使用します。
db.define_table('person', Field('name'), Field('number_of_dog', 'integer', default=0, writable=False), format='%(name)s') db.define_table('dog', Field('owner_id', db.person), Field('name'))
属性の設定に、次の関数を使用します。
def check_name(name): for s in name.split(): if len(s) >= 3: return False return True名前のチェックを行います。空白文字を除いて、3文字以上連続する文字列があるかどうかをチェックします。
def check_dog(id): return False if db(db.dog.owner_id==id).isempty() else Truedogテーブルに指定するowner_idの、レコードが存在するかどうかをチェックします。
def count_dog(owner_id): db(db.person.id==owner_id).update(number_of_dog=db(db.dog.owner_id==owner_id).count()) return None指定するowner_idのdogレコード数を集計し、personレコードのnumber_of_dogフィールドに格納します。
def store_owner_id(owner_id): if not session.check_owner_id: session.check_owner_id =[owner_id] else: session.check_owner_id.append(owner_id) return None(dogレコード更新・削除前に)レコードのowner_idの値を、セッションに格納します。
def check_count_dog(): if session.check_owner_id: for owner_id in session.check_owner_id[:]: count_dog(owner_id) session.check_owner_id.remove(owner_id) return None(dogレコード更新・削除後に)セッションに格納されたowner_idを使用し、dogレコード数を集計し、personレコードのnumber_of_dogフィールドを更新します。
属性を次のように設定してみます。
db.person._before_insert.append(lambda dict: check_name(dict['name']) if 'name' in dict else True) db.person._before_update.append(lambda set,dict: check_name(dict['name']) if 'name' in dict else False) db.person._before_delete.append(lambda set: check_dog(set.select().first().id)) db.dog._after_insert.append(lambda row,id: count_dog(row.owner_id)) db.dog._before_update.append(lambda set,dict: store_owner_id(set.select().first().owner_id) if 'owner_id' in dict else False) db.dog._after_update.append(lambda set,dict: store_owner_id(dict['owner_id']) if 'owner_id' in dict else None) db.dog._after_update.append(lambda set,dict: check_count_dog()) db.dog._before_delete.append(lambda set: store_owner_id(set.select().first().owner_id)) db.dog._after_delete.append(lambda set: check_count_dog())
この設定により、レコードの挿入・更新・削除に伴い、次の表のように動作します。
テーブル レコード操作 タイミング 動作説明 person insert 前 名前の長さチェックを行います。 チェックを通過しない場合は挿入しません。 person update 前 名前の長さチェックを行います。 チェックを通過しない場合は更新しません。 person delete 前 関連するdogレコードがないかチェックします。 dogレコードがある場合は削除しません。 dog insert 後 同じ飼い主のdogレコード数を集計し、personテーブルのnumber_of_dogフィールドを更新します。 dog update 前 更新前レコードのowner_idをセッション変数に保存します。 dog update 後 更新したowner_idをセッション変数に保存します。 dog update 後 セッション変数に保存されたowner_idのdogレコード数を集計し、personテーブルのnumber_of_dogフィールドを更新します。 dog delete 前 削除レコードのowner_idをセッション変数に保存します。 dog delete 後 セッション変数に保存されたowner_idのdogレコード数を集計し、personテーブルのnumber_of_dogフィールドを更新します。
- dogテーブルのレコード更新では、前1回後2回の3回動作します。更新前後の飼い主でそれぞれ集計するためです。
- dogテーブルのレコード削除の場合は、前と後で2回動作します。これは削除後だけではレコードが消えてしまうので、飼い主情報がわからなくなるためです。
- dogテーブルのレコード操作により、コールバック関数でpersonテーブルのレコードも更新します。このため、personテーブルに設定したコールバック関数も同時に動作します。
またコールバック属性に登録した関数は、戻り値を返すことができますが、値は None
、 True
、False
のみ可です。レコード操作の前に(before)動作する属性では、True
の戻り値を返ってきた場合、実際のレコード操作は行われません。
設定したコールバック関数を起動させないでレコードを更新する場合は、 update_naive()
メソッドを使用してください。
>>> db(db.dog.id==12).update_naive(owner_id=5) 1