wiki.zope.jp
CMFFormController
   
RecentChanges WikiHelp WikiPractice JumpSearch

直感で訳しているので誰か直してください。お願いします。

Using the CMFFormController

CMFFormController パッケージはフォームの妥当性検査を単純化することで 開発を楽にします。また、サイト管理者はコードを変更すること無しに パッケージの振る舞いをオーバーライドすることも簡単になり、 変更点を配布しないでパッケージをアップグレードすることが簡単になります。

How it works:

Forms

CMFFormController の機能を使用するには、普通のページテンプレート ではなくコントローラーページテンプレートを使用する必要があります。 コントローラーページテンプレートは普通のページテンプレートのように 振舞いますが、表示されるときにはいくつかの拡張された動作を行います。

CMFFormController を使用した基本的なフォームは次のとおりです:

        <form tal:define="errors options/state/getErrors"
              tal:attributes="action string:${here/absolute_url}/${template/id};"
             method="post">
           <input type="hidden" name="form.submitted" value="1" />
           <p tal:define="err errors/foo|nothing" tal:condition="err" tal:content="err" />
           <input type="text"
                  name="foo"
                  tal:define="val request/foo|nothing"
                  tal:attributes="value val" />
           <input type="submit" name="submit" value="submit" />
        </form>

以下に注目してください。

このフォームを使用する前に、フォームの変数を検査するバリデータを 指定する必要があり、妥当性検査のあとに起こるアクションを指定する必要があります。

バリデータを指定する

バリデータを指定する方法は基本的なものが二つあります。

  1. .metadata プロパティファイルでファイルシステムベースの コントローラーページテンプレートのバリデータを指定します
  2. ZMI(またはプログラムによって)バリデータを指定します。 これらの値はZODB上の portal_form_controller オブジェクトの 属性として保存されます。

バリデータを双方の場所で指定した場合、ZMIで指定したものが formで指定したものよりも優先されます。

ファイルシステム上でバリデータを指定する

ファイルシステム上に .metadata プロパティファイルのオブジェクトを 使用してバリデータを指定することができます。

.metadata ファイルを作成するためには単純にページテンプレート と同じ名前に .medatada を最後に追加した名前のファイルを作成します。 例えば、コントローラーページテンプレートが document_edit_form.cpt という名前だとします。プロパティは document_edit_form.cpt.metadata というファイルに保存することになります。

.medatada ファイルはPython標準の ConfigParser? の文法を使用します。 .medatada ファイルの validators セクションは次のようになります:

           [validators]
           validators = validate_script1, validate_script2

妥当性検査スクリプトの validate_script1validate_script2 は 順番に呼び出されます。

型指定バリデータ

フォームの持つコンテキストのタイプによって異なるバリデータを 呼び出したいことがあると思います。

その場合は以下のように書きます:

            [validators]
            validators = validate_script1
            validators.Document = validate_script2

上の例では、コンテキストが Document オブジェクトであれば妥当性検査のためには 'validate_script2'が呼び出され、それ以外には validate_script1 が 呼び出されます。

バリデータが指定される順番が問題とならないことに注意してください。 双方が有効な場合、型を指定したバリデータは型を指定しないバリデータを オーバーライドします。

ボタン指定のバリデータ

フォームに異なるボタンが二つあり、どちらのボタンが押されたかによって 別々の妥当性検査を行いたい場合があると思います。これは 次のように実現できます:

最初に、ボタンに button1、 button2 と名前をつけます:

               <input type="submit"
                      name="form.button.button1"
                      value="First Button" />
               <input type="submit"
                      name="form.button.button2"
                      value="Second Button" />

次に、.medadata ファイルでbutton1とbutton2のバリデータを指定します:

               [validators]
               validators..button1 = validate_script1, validate_script3
               validators..button2 = validate_script2, validate_script4

.. という表記に注意してください。これは型を指定するための プレースホルダーです。更に、コンテキストが Document で button2 が 押されたときに validate_script5 が呼ばれるようにするには以下を 追加することで可能となります:

               [validators]
               validators.Document.button2 = validate_script5

ボタンを指定したバリデータは指定しないバリデータをオーバーライド することを覚えておいてください。

ZMI でバリデータを指定する

ZMIでコントローラーページテンプレートを見ると、Varidation と Actions の二つのタブが追加された普通のページテンプレートの物のような ページが見えるでしょう。 Validation タブをクリックしてください。

Validation タブは対象のページテンプレートのすべての バリデータを表示します。 WEBのフォーム上から専用のオプションを 用いてバリデータを指定することができます。

すべてのフォームのバリデータの情報はポータルサイトの portal_form_controller に保存されます。これは情報が ZODB上に永続化されているためファイルシステム上のバリデータを 指定しても問題が発生しないということを意味します。 バリデータの情報はフォームのIDに束縛されているため、 同じIDを持つフォームは同じバリデータを使用することに注意してください。 このため複数のスキンがある場合でも単純さを保つことができます。

*同じIDのフォームは同じバリデータを使用し、どのスキンに存在するかは 問題ではない*

フォームがサブミットされたとき、最初にZMIで指定された有効なバリデータが 存在するかをチェックします。見つかった場合にはそれを使用します。 ZMIで指定したバリデータが見つからなかった場合には REQUEST オブジェクト の hidden 変数でバリデータが指定されているかをチェックします。 その結果、ZMIで指定されたバリデータはフォームで指定されたものよりも 優先されます。

プログラムでバリデータを指定する

ポータルの portal_form_controller ツールにはコントロールページ テンプレートに対してバリデータを指定するメソッドが用意されています。 APIは以下のとおり:

              portal_form_controller.addFormValidators(id,
                                                       context_type,
                                                       button,
                                                       validators)

id はコントローラーページテンプレートのID、 context_type はコンテクストオブジェクトのクラスの名前 button は押されるボタンの名前、 validators はカンマで区切られた文字列またはリストです。 どのクラスにもバリデータを動作させたい場合には context_type に None を 指定します。同様に、どのボタンに対してもバリデータを動作させたい場合には button に None をセットします。

アクションを指定する

バリデータの並びは実行されるとstateオブジェクトにステータスを 返します。 デフォルトのステータスは success なので、どのバリデータも 実行されなかった場合は success となります。バリデータが エラーに出会うと、一般的にはステータスに failure を設定します。 フォームに対して次に行うべきことは、戻されたステータスに対する 動作を指定することです。

バリデータに対してフォームのアクションを指定する基本的な方法が 二つあります。

  1. ファイルシステム上のコントローラーページテンプレートと コントローラーPythonScriotに対して .metadata プロパティで アクションを指定できます。
  2. ZMI(またはプログラムで)アクションを指定できます。 これらの値はZODB上の portal_form_controller の属性として 保存されます。

双方の場所でアクションを指定した場合、ZMIで指定したものがform で指定したものよりも優先されます。

ファイルシステム上でアクションを指定する

ファイルシステムでオブジェクトの .metadata プロパティファイルを 使用してアクションを指定することができます。

アクションはバリデータと同じ .metadata ファイルに保存します。 ファイルの中の actions セクションの文法は以下のようなものです:

           [actions]
           action.success = traverse_to:string:script1

上の例では、フォームがサブミットされて妥当性検査スクリプトがステータス success を返したとき、traverse_to アクションが string:script1 という引数で実行されます。よって、フォームのデータが妥当ならば script1 というスクリプトが実行されます。代わりに、 action.success = redirect_to:string:http://my_url_here という指定を行ってブラウザに http://my_url_here に リダイレクトを引き起こすことができます。

ステータス failure のデフォルトのアクションは現在のフォームを リロードさせます。フォームはオプションとして渡るstateオブジェクト を介してすべてのエラーメッセージにアクセスすることができます。

型に固有のアクション

フォームが持つコンテキストの型によって異なるアクションを 引き起こしたいことがあると思います。

これは以下のようにして実現します:

                  [actions]
                  action.success = traverse_to:string:script1
                  action.success.Document = traverse_to:string:document_script

上の例では、コンテキストがDocumentオブジェクトの場合は 妥当性検査が成功したときに document_script を実行し、 そのほかの場合はすべて script1 が実行されます。 変数を指定する順番が問題にならない(両方指定されていると 型を指定したアクションが指定しないアクションをオーバーライドする) ことに注意してください。

ボタンに固有のアクション

フォームに二つの異なるボタンがあるとき、どちらのボタンが 押されたかによって別のアクションを起こしたいことがあると思います。 これは以下のようにして実現できます:

最初に、ボタンに button1、button2 と名前をつけます:

               <input type="submit"
                      name="form.button.button1"
                      value="First Button" />
               <input type="submit"
                      name="form.button.button2"
                      value="Second Button" />

次に、button1 と button2 のアクションを指定します:

               [actions]
               action.success..button1 = traverse_to:string:script1
               action.success..button2 = traverse_to:string:script2

.. という表記に注意してください。これは型を指定する場合のための プレースホルダーです。更に、コンテキストが Document で button2 が 押されたときに validate_script2 が呼ばれるようにするには以下を 追加することで可能となります:

               [actions]
               action.success.Documnet.button2 = traverse_to:string:document_script2

ZMI でアクションを指定する

ZMIでコントローラーページテンプレートを見ると、Varidation と Actions の二つのタブが追加された普通のページテンプレートの物のような ページが見えるでしょう。 Actions タブをクリックしてください。

Actions タブは対象のページテンプレートのすべての アクションを表示します。 WEBのフォーム上から専用のオプションを 用いてアクションを指定することができます。

すべてのフォームのアクションの情報はポータルサイトの portal_form_controller に保存されます。これは情報が ZODB上に永続化されているためファイルシステム上のアクションを 指定しても問題が発生しないということを意味します。 アクションの情報はフォームのIDに束縛されているため、 同じIDを持つフォームは同じアクションを使用することに注意してください。 このため複数のスキンがある場合でも単純さを保ます。 (同じIDのフォームは同じアクションを使用し、どのスキンであるかは 影響しない)

フォームがサブミットされたとき、最初にZMIで指定された有効なアクションが 存在するかをチェックします。見つかった場合にはそれを使用します。 ZMIで指定したアクションが見つからなかった場合には REQUEST オブジェクト の hidden 変数でアクションが指定されているかをチェックします。 その結果、ZMIで指定されたアクションはフォームで指定されたものよりも 優先されます。

プログラムでアクションを指定する

ポータルの portal_form_controller ツールにはコントロールページ テンプレートに対してアクションを指定するメソッドが用意されています。 APIは以下のとおり:

               portal_form_controller.addFormAction(id,
                                                    status,
                                                    context_type,
                                                    button,
                                                    action_type,
                                                    args)

id はコントローラーページテンプレートのID、 status はどのアクションを実行するかを決めるステータス、 context_type はコンテクストオブジェクトのクラスの名前 button は押されるボタンの名前、 action_type は引き起こされるアクションの種類、 args は文字列(一般的にTALES文)でアクションに渡されます。 どのクラスにもアクションを動作させたい場合には context_type に None を 指定します。同様に、どのボタンに対してもアクションを動作させたい場合には button に None をセットします。

Validation Scripts

妥当性検査のスクリプトを書く時は、Python Script の代わりに Controller Validators を使用してください。 Controller Validators は普通のスクリプト対して ZMI のAction タブが 追加されています。ファイルシステム上では、Controller Validators は .py ではなく .vpy の拡張子を使用します。

REQUEST の値 n が整数であることをテストする基本的な妥当性検査スクリプト を見てみましょう:

         n = context.REQUEST.get('n', None)
         if not n:
            state.setError('n', 'Please enter a value', new_status='failure')
         else:
            try:
               int(n)
            except ValueError:
               state.setError('n', 'Please enter an integer',
                              new_status='failure')

         if state.getErrors():
            state.set(portal_status_message='Please correct the errors shown.')
         return state

はじめに気をつけることは、Controller Validators は state という名の 組み込みの状態オブジェクトを持っている点です。この状態オブジェクト (ControllerState?クラス)は妥当性検査のチェインの中で何が起こったかに 関する基本的な情報を含んでいます。

状態オブジェクトは status という属性を持ち、ここに現在の妥当性検査の状態を 保持しています。最初の状態は success です。バリデータによってエラーが検出 されると、他の状態(一般的にはfailure)に変更されます。

また、状態オブジェクトは検出したエラーも誇示しています。setError メソッドが ある変数に対するエラーメッセージを設定するために使用されます。 setError メソッドはオプションの引数 new_status をもち、エラーメッセージの 設定と状態の更新を同時に行うことができます。 ある変数に対するエラーメッセージがすでに登録されているかどうかは state.getError(variable_name) を呼び出すことで判別できます。

set メソッドでは状態オブジェクトの複数の属性を一度に変更できます:

         state.set(status='my_new_status')

また、 set メソッドを介して状態オブジェクトにキーワード付き引数を 渡すことができます。これらの引数はアクションによって同時に渡されます。 traverse_to アクションはこれらのキーワード付き引数をREQUESTの 中に配置します。redirect_to アクションはリダイレクする URL に クエリー文字列として追加します。

最後に、状態オブジェクトを返します。

スクリプト

フォームの妥当性検査の後に何かの処理を行うスクリプトを書く場合、 通常のPythonScriptのかわりにController Python Scriptsを使用して サイト管理者がZMI上からアクションをオーバーライドすることができます。 ファイルシステム上の Controller Python Scripts には拡張子 .py ではなく .cpy を使用します。Controller Validators と Controller Python Scripts には重要な違いがあることに注意してください。 適当なスクリプトの型(Controller Validator または Controller Python Script) および/または、適当なファイルの拡張子(.cpy または .vpy)を使用してください。

contextの属性にREQUEST経由で渡された変数 n の値を設定するき基本的な スクリプトを見てみましょう:

      context.n = context.REQUEST.get('n')

      # Optionally set the default next action (this can be overridden
      # in the ZMI)

      state.setNextAction('redirect_to:string:view')

      # Optionally pass a message to display to the user
      state.setKwargs({'portal_status_message':'You set context.n to %s.' % str(context.n)})
      return state

このスクリプトを呼び出すためには通常 traverse_to アクションを使用する ことに注意してください。これはREQUESTオブジェクトに設定されたフォームの値が スクリプトから使用できることを保証します。

このスクリプトは自身のアクションを現在のコンテキストの対する間接URL view に リダイレクトします。ステータスはセットされないので、 デフォルトの success となります。

state.setNextAction の指定は .metadata file での以下の指定に似ています:

        [actions]
        action.success = redirect_to:string:view

.metadata ファイルでの指定と同様にスクリプトで指定されたデフォルトのアクションは ZMIでオーバーライドされます。このため、サイト管理者はコードを変更せずに 実行後スクリプトを上書きできるようになります。

最後に、state オブジェクトを返します。

Script の妥当性検査

個別の妥当性検査スクリプトを持つということは、一般的に 妥当性検査がスクリプトから移動されていることを意味します。 これはスクリプトを単純化しますが、無効なデータで 直接呼び出すことが可能であるということでもあります。 スクリプトに対してバリデータを追加することでこの問題を防ぐことができます。 Controller Python Scripts は Controller Page Templates と 同様の ZMI および .metadata ファイルの仕組みを使用します。

バリデータは呼び出されるたびに状態オブジェクトに呼び出しを記録します。 妥当性検査はそれがフォームから呼び出されたときにスクリプトから 重複して呼び出されないようになっています。

スクリプトにバリデータを関連付けた場合、スクリプトはデフォルトで はアクションを設定しないので識別できる failure ステータスの アクションを設定する必要があることに気をつけてください。 自分のスクリプトが失敗した場合に'script_failure'のような別のステータスを 定義したくなるでしょう。そうすればスクリプトが無効なデータによって 失敗した場合の振舞いを定義することができるようになります。

Last edited Sun, 10 Sep 2006 22:37:22 +0900 Edit this page