FORM

□未翻訳

□翻訳中

■翻訳完了(細田謙二)

■レビュー(Omi Chiba)

FORM

次のような"default.py"コントローラを持つtestアプリケーションを考えます:

Consider as an example a test application with the following "default.py" controller:

1.

2.

def display_form():

return dict()

関連付けるビュー"default/display_form.html"は以下のようにします:

and the associated "default/display_form.html" view:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

{{extend 'layout.html'}}

<h2>Input form</h2>

<form enctype="multipart/form-data"

action="{{=URL()}}" method="post">

Your name:

<input name="name" />

<input type="submit" />

</form>

<h2>Submitted variables</h2>

{{=BEAUTIFY(request.vars)}}

これは、ユーザー名の尋ねる一般的なHTMLフォームです。このフォームを入力しサブミット・ボタンをクリックすると、フォームは自分自身をサブミットし、request.vars.name変数とその値が下部に表示されます。

This is a regular HTML form that asks for the user's name. When you fill the form and click the submit button, the form self-submits, and the variable request.vars.name and its value is displayed at the bottom.

同じフォームをヘルパを用いて生成することができます。これは、ビュー、または、アクションにおいて行うことができます。web2pyはフォームの処理をアクションにおいて行うので、フォームをアクションで定義しても差し支えありません。

You can generate the same form using helpers. This can be done in the view or in the action. Since web2py processed the form in the action, it is OK to define the form in the action.

これが新しいコントローラです:

Here is the new controller:

1.

2.

3.

def display_form():

form=FORM('Your name:', INPUT(_name='name'), INPUT(_type='submit'))

return dict(form=form)

関連付けるビュー"default/desplay_form.html"は以下のようにします:

and the associated "default/display_form.html" view:

1.

2.

3.

4.

5.

{{extend 'layout.html'}}

<h2>Input form</h2>

{{=form}}

<h2>Submitted variables</h2>

{{=BEAUTIFY(request.vars)}}

以前のコードは上のコードと等しいです。しかし、フォームは、FORMオブジェクトをシリアライズする{{=form}}という文によって生成されています。

The code so far is equivalent to the previous code, but the form is generated by the statement{{=form}} which serializes the FORM object.

次に、フォームの検証と処理を追加して、一段複雑なものを加えます。

Now we add one level of complexity by adding form validation and processing.

コントローラを以下のように変更します:

Change the controller as follows:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

def display_form():

form=FORM('Your name:',

INPUT(_name='name', requires=IS_NOT_EMPTY()),

INPUT(_type='submit'))

if form.accepts(request.vars, session):

response.flash = 'form accepted'

elif form.errors:

response.flash = 'form has errors'

else:

response.flash = 'please fill the form'

return dict(form=form)

関連付けるビュー"default/desplay_form.html"は以下のようにします:

and the associated "default/display_form.html" view:

1.

2.

3.

4.

5.

6.

7.

8.

9.

{{extend 'layout.html'}}

<h2>Input form</h2>

{{=form}}

<h2>Submitted variables</h2>

{{=BEAUTIFY(request.vars)}}

<h2>Accepted variables</h2>

{{=BEAUTIFY(form.vars)}}

<h2>Errors in form</h2>

{{=BEAUTIFY(form.errors)}}

以下のことに注意してください:

Notice that:

  • アクションでは、入力フィールド"name"に対してrequires=IS_NOT_EMPTY()バリデータを加えています。

  • In the action, we added the requires=IS_NOT_EMPTY() validator for the input field "name".

  • アクションでは、form.accepts(...)の呼び出しを加えています。

  • In the action, we added a call to form.accepts(...)

  • ビューでは、フォームとrequest.varsとともにform.varsとform.errorsを表示しています。

  • In the view, we are printing form.vars and form.errors as well as the form andrequest.vars.

すべての作業は、formオブジェクトのacceptsメソッドによって行われます。これは、request.varsを、(バリデータによって表現された)宣言された要求に従って、フィルタします。acceptsは、検証を通ったこれらの変数をform.varsに格納します。フィールドの値が何かしら要求を満たさない場合は、失敗したバリデータがエラーを返し、そのエラーがform.errorsに格納されます。form.varsとform.errorsは両方とも、request.varsに似たgluon.storage.Storageオブジェクトです。前者は、次のように検証を通った値を保持します:

All the work is done by the accepts method of the form object. It filters the request.varsaccording to the declared requirements (expressed by validators). accepts stores those variables that pass validation into form.vars. If a field value does not meet a requirement, the failing validator returns an error and the error is stored in form.errors. Both form.vars andform.errors are gluon.storage.Storage objects similar to request.vars. The former contains the values that passed validation, for example:

1.

form.vars.name = "Max"

後者は、次のようにエラーを保持します:

The latter contains the errors, for example:

1.

form.errors.name = "Cannot be empty!"

acceptsメソッドのすべての用法は以下の通りです:

The full signature of the accepts method is the following:

1.

2.

3.

form.accepts(vars, session=None, formname='default',

keepvalues=False, onvalidation=None,

dbio=True, hideerror=False):

これらオプション・パラメータの意味は、次の小節で説明します。

The meaning of the optional parameters is explained in the next sub-sections.

accepts関数はフォームが受理されたときにTrueを返し、そうでない場合はFalseを返します。フォームは、エラーがある場合か、サブミットされてない場合(たとえば、最初に表示されるとき)には受理されません。

The accepts function returns True if the form is accepted and False otherwise. A form is not accepted if it has errors or when it has not been submitted (for example, the first time it is shown).

次に示すのは、このページが最初に表示されたときの様子です:

Here is how this page looks the first time it is displayed:

無効なサブミットをしたときの様子です:

Here is how it looks upon invalid submission:

有効なサブミットをしたときの様子です:

Here is how it looks upon a valid submission:

隠しフィールド

Hidden fields

上記のフォーム・オブジェクトが{{=form}}によってシリアライズされたとき、acceptsメソッドに対する前述の呼び出しがあるために、フォームは次のようになります:

When the above form object is serialized by {{=form}}, and because of the previous call to theaccepts method, it now looks like this:

1.

2.

3.

4.

5.

6.

7.

<form enctype="multipart/form-data" action="" method="post">

your name:

<input name="name" />

<input type="submit" />

<input value="783531473471" type="hidden" name="_formkey" />

<input value="default" type="hidden" name="_formname" />

</form>

注意する点は、2つの隠しフィールド"_formkey"と"_formname"があることです。これらの存在は、acceptsの呼び出しによって引き起こされたもので、2つの異なる重要な役割を果たします:

Notice the presence of two hidden fields: "_formkey" and "_formname". Their presence is triggered by the call to accepts and they play two different and important roles:

  • "_formkey"という隠しフィールドは一度限りのトークンで、web2pyがフォームの二重投稿を防ぐために用いられます。このキーの値はフォームがシリアライズされたときに生成され、sessionに保存されます。フォームがサブミットされたときに、この値が一致する必要があります。そうでないとacceptsは、フォームが全くサブミットされてないかのように、エラーなしでFalseを返します。これは、フォームが正しくサブミットされたかどうかをweb2pyが判断できないためです。

  • The hidden field called "_formkey" is a one-time token that web2py uses to prevent double submission of forms. The value of this key is generated when the form is serialized and stored in the session. When the form is submitted this value must match, or else accepts returns False without errors as if the form was not submitted at all. This is because web2py cannot determine whether the form was submitted correctly.

  • "_formname"という隠しフィールドは、フォームの名前としてweb2pyによって生成されますが、その名前は上書きすることができます。このフィールドは、ページが複数のフォームを含んで処理することを可能にするために必要です。web2pyは、この名前によって異なるサブミットされたフォームを区別します。

  • The hidden field called "_formname" is generated by web2py as a name for the form, but the name can be overridden. This field is necessary to allow pages that contain and process multiple forms. web2py distinguishes the different submitted forms by their names.

  • オプション的な隠しフィールドはFORM(..,hidden=dict(...))のように指定します。

  • Optional hidden fields specified as FORM(..,hidden=dict(...)).

これらの隠しフィールドの役割と、カスタムフォームと複数のフォームを持つページにおける使用方法は、本章の後半で詳しく説明します。

The role of these hidden fields and their usage in custom forms and pages with multiple forms is discussed in more detail later in the chapter.

上記のフォームを空の"name"フィールドでサブミットした場合、フォームは検証を通過しません。フォームが再びシリアライズされるときは、次のように表示されます:

If the form above is submitted with an empty "name" field, the form does not pass validation. When the form is serialized again it appears as:

1.

2.

3.

4.

5.

6.

7.

8.

<form enctype="multipart/form-data" action="" method="post">

your name:

<input value="" name="name" />

<div class="error">cannot be empty!</div>

<input type="submit" />

<input value="783531473471" type="hidden" name="_formkey" />

<input value="default" type="hidden" name="_formname" />

</form>

シリアライズしたフォームにあるDIVのクラス"error"の存在に注意してください。web2pyはこのエラーメッセージをフォームに挿入し、検証を通過しなかったフィールドについて訪問者に知らせます。サブミットの際のacceptsメソッドは、フォームがサブミットされたかどうかを判断し、フィールド"name"が空でないか、また、それが要求されているかをチェックし、最終的に、バリデータからフォームにエラーメッセージを挿入します。

Notice the presence of a DIV of class "error" in the serialized form. web2py inserts this error message in the form to notify the visitor about the field that did not pass validation. Theaccepts method, upon submission, determines that the form is submitted, checks whether the field "name" is empty and whether it is required, and eventually inserts the error message from the validator into the form.

基底の"layout.html"ビューは、DIVクラスの"error"を処理することが想定されいます。デフォルトのレイアウトはjQueryのエフェクトを使用して、エラーを可視化し、赤い背景とともにスライドダウンさせます。詳細は第10章を参照してください。

The base "layout.html" view is expected to handle DIVs of class "error". The default layout uses jQuery effects to make errors appear and slide down with a red background. See Chapter 10 for more details.

keepvalues

オプション引数keepvaluesは、フォームが受理され、かつ、リダイレクトがないときに、web2pyに何をするか知らせ、同じフォームが再び表示されるようにします。デフォルトではすべてクリアされます。keepvaluesがTrueの場合、フォームは前回挿入した値を事前に入力します。これは、複数の似たレコードを繰り返し挿入するために使用することを想定したフォームがあるときに便利です。dbio引数がFalseの場合、web2pyは、フォームを受理した後、いかなるDBの挿入/更新も行いません。hideerrorがTrueでフォームにエラーが含まれている場合、フォームがレンダリングされたときにエラーは表示されません(form.errorsをどのように表示するかは開発者次第です)。onvalidation引数は以下に説明します。

The optional argument keepvalues tells web2py what to do when a form is accepted and there is no redirection, so the same form is displayed again. By default the form is cleared. Ifkeepvalues is set to True, the form is pre-populated with the previously inserted values. This is useful when you have a form that is supposed to be used repeatedly to insert multiple similar records. If the dbio argument is set to False, web2py will not perform any DB insert/update after accepting form. If hideerror is set to True and the form contains errors, these will not be displayed when the form is rendered (it will be up to you to display them from form.errors somehow. The onvalidation argument is explained below.

onvalidation

onvalidation引数はNoneもしくは、フォームを受け取り何も返さない関数をとることができます。そのような関数は、検証(が通った)直後に、かつ、それ以外のことが発生する前に呼ばれ、フォームを渡します。この関数の目的は複数あります。これは、たとえば、追加的なフォームのチェックを実行したり、最終的にフォームにエラーを加えたりすることができます。これはまた、いくつかのフィールドの値を、他のフィールドの値に基づいて計算するのに使用することもできます。これを用いて、レコードが作成/更新される前にいくつかのアクション(emailの送信など)を引き起こすことも可能です。

The onvalidation argument can be None or can be a function that takes the form and returns nothing. Such a function would be called and passed the form, immediately after validation (if validation passes) and before anything else happens. The purpose of this function is multifold. It can be used, for example, to perform additional checks on the form and eventually add errors to the form. It can also be used to compute the values of some fields based on the values of other fields. It can be used to trigger some action (like sending an email) before a record is created/updated.

以下がその例です。

Here is an example:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

db.define_table('numbers',

Field('a', 'integer'),

Field('b', 'integer'),

Field('c', 'integer', readable=False, writable=False))

def my_form_processing(form):

c = form.vars.a * form.vars.b

if c < 0:

form.errors.b = 'a*b cannot be negative'

else:

form.vars.c = c

def insert_numbers():

form = SQLFORM(db.numbers)

if form.accepts(request.vars, session,

onvalidation=my_form_processing):

session.flash = 'record inserted'

redirect(URL())

return dict(form=form)

フォームとリダイレクト

Forms and redirection

フォームを使用する最も一般的な方法は、自己サブミットを介して、サブミットされたフィールドの変数が、フォームを生成したものと同じアクションによって処理されるようにすることです。フォームが一旦受理されれば、現在のページを再び表示することはあまりありません(ここでは説明を単純にするためいくつか行っています)。訪問者を"next"ページへリダイレクトさせるのがより一般的です。

The most common way to use forms is via self-submission, so that the submitted field variables are processed by the same action that generated the form. Once the form is accepted, it is unusual to display the current page again (something we are doing here only to keep things simple). It is more common to redirect the visitor to a "next" page.

ここでは新しいコントローラの例を示します:

Here is the new example controller:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

def display_form():

form = FORM('Your name:',

INPUT(_name='name', requires=IS_NOT_EMPTY()),

INPUT(_type='submit'))

if form.accepts(request.vars, session):

session.flash = 'form accepted'

redirect(URL('next'))

elif form.errors:

response.flash = 'form has errors'

else:

response.flash = 'please fill the form'

return dict(form=form)

def next():

return dict()

現在のページの代わりにnextページでflashを設定するために、session.flashをresponse.flashの代わりに設定する必要があります。web2pyはリダイレクト後、前者を後者に移します。session.flashはsession.forget()を使用していない必要があることに注意してください。

In order to set a flash on the next page instead of the current page you must usesession.flash instead of response.flash. web2py moves the former into the latter after redirection. Note that using session.flash requires that you do not session.forget().

ページ毎に複数のフォーム

Multiple forms per page

この節の内容は、FORMとSQLFORMオブジェクトどちらにも適用されます。ページ毎に複数のフォームを持つことが可能です。しかし、web2pyにそれらを区別できるようにしなければなりません。異なるテーブルのSQLFORMによって生成されたものならば、web2pyは異なる名前を自動的にそれらに与えます。それ以外の場合は、異なるフォームの名前を明示的に与えなければなりません。以下がその例です。

The content of this section applies to both FORM and SQLFORM objects. It is possible to have multiple forms per page, but you must allow web2py to distinguish them. If these are derived by SQLFORM from different tables, then web2py gives them different names automatically; otherwise you need to explicitly give them different form names. Here is an example:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

def two_forms():

form1 = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()),

INPUT(_type='submit'))

form2 = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()),

INPUT(_type='submit'))

if form1.accepts(request.vars, session, formname='form_one'):

response.flash = 'form one accepted'

if form2.accepts(request.vars, session, formname='form_two'):

response.flash = 'form two accepted'

return dict(form1=form1, form2=form2)

ここに生成された出力を示します:

and here is the output it produces:

訪問者が空のform1をサブミットした場合、form1のみがエラーを表示します。一方、訪問者が空のform2をサブミットした場合、form2のみがエラーメッセージを表示します。

When the visitor submits an empty form1, only form1 displays an error; if the visitor submits an empty form2, only form2 displays an error message.

フォームの共有

Sharing Forms

この節の内容は、FORMとSQLFORMオブジェクトどちらにも適用されます。ここで説明することは可能ですが推奨されません。自己サブミットするフォームを持つことがベストプラクティスだからです。しかし、場合によっては、フォームを送受信するアクションが異なるアプリケーションに属していて、選択肢がないことがあります。

The content of this section applies to both FORM and SQLFORM objects. What we discuss here is possible but not recommended, since it is always good practice to have forms that self-submit. Sometimes, though, you don't have a choice, because the action that sends the form and the action that receives it belong to different applications.

異なるアクションへサブミットするフォームを生成することは可能です。これは、FORMまたはSQLFORMオブジェクトの属性において、処理するアクションのURLを指定することで行われます。例:

It is possible to generate a form that submits to a different action. This is done by specifying the URL of the processing action in the attributes of the FORM or SQLFORM object. For example:

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

form = FORM(INPUT(_name='name', requires=IS_NOT_EMPTY()),

INPUT(_type='submit'), _action=URL('page_two'))

def page_one():

return dict(form=form)

def page_two():

if form.accepts(request.vars, formname=None):

response.flash = 'form accepted'

else:

response.flash = 'there was an error in the form'

return dict()

"page_one"と"page_two"は両者とも同じformを利用していて、同じことの繰り返しを避けるために、そのフォームをすべてのアクションの外側で一度だけ定義していることに注意してください。コントローラの冒頭にある共通のコード部分は、アクションの呼び出しに制御を渡す前に毎回実行されます。

Notice that since both "page_one" and "page_two" use the same form, we have defined it only once by placing it outside of all the actions, in order not to repeat ourselves. The common portion of code at the beginning of a controller gets executed every time before giving control to the called action.

"page_one"accepts を呼び出さないため、formには名前がなくキーもありません。そのため、acceptsにはsessionを渡してはならず、 formname=Noneを設定する必要があります。そうでないと、フォームは"page_two"に受け取られたときに検証を行いません。

Since "page_one" does not call accepts, the form has no name and no key, so you must not pass the session and set formname=None in accepts, or the form will not validate when "page_two" receives it.