Subsections

Form handling

イントロダクション

複雑なフォームを扱うこと、特にユーザーエラーを扱いたいと思ったときに時々苦痛になる時がままあります。

フォームモジュールは、いったん使い方を学んだら、多くの時間を費やしたりトラブルを避けることができます。

これらの時間の多くが、以下のことをやりたいためだと思います。

そして、おそらくあなたは以下のような振る舞いをフォームに持たせたいと思っているでしょう:

フォームモジュールがどのようにあなたの助けになるか見ていきましょう。。。

モジュール

このモジュールは4つのCherryClassを定義します:

FormField

FormFieldインスタンスは各フォームフィールドのために使用されます。

function: __init__(label, name, typ, mask=None, mandatory=0, size=15, optionList=[], defaultValue='', validate=None)

labelはフィールドのとなりに表示される文字列です。

nameはフィールド名を格納した文字列です。

typはフィールドのタイプを格納した文字列です。これは以下の内の一つとなります: text, password, file, hidden, submit, image, select, textarea, radio, checkbox

maskはフィールドを表示するために使われるマスクです。デフォルト値はdefaultFormMask.defaultMaskです。maskはFormFieldインスタンスを引数として受け取り、これはフィールドを表示するためになんらかのHTMLを返すはずです。

mandatoryはフィールドが入力必須か否かを示す整数です。

sizeはフィールドのサイズを示す整数です。

mandatoryはフィールドが入力必須か否かを指し示す整数です(これはテキストやパスワードなどのようなフィールドのためだけに使われます)

optionListはフィールドの異なるオプションを格納する文字列のリストです(ラジオボックスかチェックボックスフィールドのためだけに使われます)。

validateはフィールドが有効かどうかを確認するために使われる関数です。この関数はフィールドの値を引数として受け取り、もし値が適切であればNoneを返し、もし値が不適切であればエラーメッセージが入った文字列を返します。

FormSeparator

FormSeparaterインスタンスはフォーム上の異なるフィールド間のテキストやイメージを表示するために使われます。

function: __init__(label, mask)

labelは何を表示するかを知るためにmaskによって使われる文字列です。

maskはフィールドを表示するために使われるマスクです。このmaskはFormSeparotrインスタンスを引数として受け取り、セパレータを表示するためになんらかのHTMLを返します。

DefaultFormMask

このCherryClassはフィールドのmaskのデフォルトの実行が格納されています。あなたはおそらく自分でデザインしたmaskを使いたいと思います。次のセクションでは、フィールドマスクの書き方を説明します。

Form

これがモジュールのメインのCherryClassです。フォールを作成するために、Formから継承したCherryClassを宣言します。

以下の変数とメソッドを使います:

variable: method
フォームタグのmethod属性を格納した文字列です。これはsendpostのはずです。デフォルト値はpostです。

variable: enctype
フォームタグのenctype属性を格納した文字列です。たとえば、ユーザーがファイルをアップロードできるようなフォームのために、multipart/form-dataを使うということです。デフォルト値は空文字列で、これらenctype属性が無視されたことを意味します。 variable: fieldList
FormFieldとFormInstance CherryClassのインスタンスが入っているリストです。このリストはどのフィールドとセパレータが表示されるかを決め、その順番を決定するものです。fieldListはCherryClassの__init__メソッド内にセットします。

function: formView(leaveValues=0)
この関数はフォームのHTMLコードを返します。もしleaveValuesがfalseだったら、各フィールドはデフォルト値をとります。もしleaveValuesがtrueであれば、request.paramMap内の値を使います(言い替えると、この値はユーザーによって入力されたものです)。

function: validateFields()
もしあなたが同時にいくつかのフィールドを呼び出し有効かどうか確認を実行する必要がある場合(例えば、2ぐのパスワードが一致するかどうかをチェックする)、この関数は上書きされます。

もしフィールドがエラーである場合、この関数はFormFieldインスタンスのerrorMessageメンバ変数をセットします。

function: setFieldErrorMessage(fieldName, errorMessage)
fieldNameという名前のFormFieldインスタンスのerrorMessageメンバ変数をセットします。

function: getFieldOptionList(fieldName)
fieldNameという名前のFormFieldインスタンスのoptionListメンバ変数をセットします。

function: getFieldDefaultValue(fieldName)
fieldNameという名前のFormFieldインスタンスのdefaultValueメンバ変数をセットします。

function: setFieldDefaultValue(fieldName, defaultValue)
fieldNmaeという名前のFormFieldインスタンスのdefaultValueメンバ変数をセットします。

function: getFieldNameList(exceptList=[])
fieldListメンバ変数に基づいて、フィールド名のリストを返します。exceptList内の名前は無視されます。

function: validateForm()
この関数はユーザーが入力したデータが適切か不適切なデータかどうかをチェックします。適切であれば1を返し、そうでなければ2を返します。

view: postForm(**kw)
このviewはユーザーがフォームをsubmitした時に自動敵に呼び出されます。フォームデータを扱うため、このviewを上書きしたりコードを追加したりすることができます。このviewの典型例は以下ようなものです:

def postForm(self, **kw):
    if self.validateForm():
        # Yes, the data is correct
        # Do what you want here
        pass
    else:
        # No, the data is incorrect
        # Redisplay the form and tell the user to fix the errors:
        return "<html><body><font color=red>Fill out missing fields</font>"+self.formView(1)+"</body></html>"

フォームmaskを記述する

モジュールはフォームのためのデフォルトのmaskが付属します。しかし自分のデザインを津悪ためにこれを変更したいだろうと思います。あなたがやることは自分で作成するフォームmaskを記述するだけです。

フォームmaskはFormFieldインスタンスを入力としてとり、HTMLコードを出力として返します。あなたのmaskがcurrentValueメンバ変数にしたがってフィールドの値をセットすることを忘れないでください。さらに、errorMessageがセットされているかどうかでフィールドの扱いが違います。

例えば、選択ボックスについて、マスクは以下のようになります:

if field.typ=='select':
    result='%s: <select name="%s" size="%s">'%(field.label, field.name, field.size)
    for optionValue in optionList:
        if optionValue==field.currentValue: checked=' checked'
        else: checked=''
        result+='<option%s>%s</option>'%(checked,optionValue)
    result+='</select>
    if field.errorMessage:
        result+=' <font color=red>%s</font>'%field.errorMessage
return result+'<br>'

一緒に使う

すてきなフォームを作成するために、これらのCherryClass、変数、メソッドの使い方をみていきましょう。

ユーザーがログインやパスワードを選択し、e-mailを入力したり、住んでいる国や、趣味を入力するフォームを作成することにします。

これには6つのフィールドが必要です:

これに加え、e-mailフィールドと住んでいる国のフィールドの間に一行入れることにします。

以下のようなコードになります:

use Form, MaskTools

# We start by creating a CherryClass that inherits from Form
# This CherryClass will hold all the informations about the form we want to create
CherryClass MyForm(Form):
function:
    def __init__(self):
        # Instantiate all fields plus 3 separators (one at the beginning, one for the line and one a
t the end)
        headerSep=FormSeparator('', defaultFormMask.defaultHeader)
        login=FormField(label='Login:', name='login', mandatory=1, typ='text')
        password=FormField(label='Password:', name='password', mandatory=1, typ='password')
        password2=FormField(label='Confirm password:', name='password2', mandatory=1, typ='password'
)
        email=FormField(label='E-mail:', name='email', typ='text', validate=self.validateEmail)
        lineSep=FormSeparator('', self.lineSeparator)
        country=FormField(label='Country:', name='country', typ='select', optionList=['USA', 'Andorr
a', 'Lichtenstein', 'CherryPyLand'], defaultValue='USA')
        hobbies=FormField(label='Hobbies:', name='hobbies', typ='checkbox', optionList=['Using Cherr
yPy', 'Eating Cherry Pie'])
        submit=FormField(label='', name='Submit', typ='submit')
        footerSep=FormSeparator('', defaultFormMask.defaultFooter)
        self.fieldList=[headerSep, login, password, password2, email, lineSep, country, hobbies, sub
mit, footerSep]

    # Function that checks if an e-mail is correct or not
    def validateEmail(self, email):
        try:
            before, after=email.split('@')
            if not before or after.find('.')==-1: raise 'Error'
        except: return "Wrong email"

    # Function that performs general validation of the form. In our case, we need to check
    # that the passwords match
    def validateFields(self):
        # Warning: paramMap could have no "password" or "password2" key if the user didn't fill out
the fields
        if request.paramMap.get('password','')!=request.paramMap.get('password2',''):
            # Set errorMessage for password fields
            self.setFieldErrorMessage('password', 'Not matching')
            self.setFieldErrorMessage('password2', 'Not matching')

mask:
    # Line separator used to draw a line between the email field and the country field
    def lineSeparator(self, label):
        <tr><td colspan=3 height=1 bgColor=black py-eval="maskTools.x()"></td></tr>

view:
    def postForm(self, **kw):
        if self.validateForm():
            return root.formOk()
        else:
            return "<html><body><font color=red>Please correct the errors (fields in red)</font>"+se
lf.formView(1)+"</body></html>"


# Now we just have to create a regular Root CherryClass, that will call some of MyForm's methods
CherryClass Root:
mask:
    def index(self):
        <html><body>
            Welcome, please fill out the form below:
            <py-eval="myForm.formView()">
        </body></html>
    def formOk(self):
        <html><body>
            Thank you for filling out the form.<br>
            All values were correct
        </body></html>


Debian User 2003-10-13