[TurboGears] ブログを作成する。その5
注意
この記事は、id:SumiTomohiko:20070119:1169220734の続きです。
入力値検証
今回は、フォームで入力された値を検証する機能を追加します。検証には、ウィジェットを使用します。ウィジェットのフィールドには、ヴァリデータを設定することができるのです。また、「その4」ではウィジェットは使うたびに生成していたのですが、入力値検証ではインスタンスが必要なので、その点も修正します。
作成するウィジェットは2種類あります。登録画面のフォームのウィジェットと、編集画面のフォームのウィジェットです。これら2つには、以下の違いがあります。
- 編集画面には、編集対象のレコードを保持するidフィールドがある。登録画面にはない。
- 登録画面の遷移先は/doaddであり、編集画面の遷移先は/doeditである。
これら以外の共通点は基底クラスでまとめ、差異だけ派生クラスで実装します。
まず、必要なモジュールをインポートします。具体的には、turbogears.error_handlerとturbogears.validate, turbogears.validatorsです。
from turbogears import controllers, expose, widgets, error_handler, validate from turbogears import validators, identity, redirect
ウィジェットの共通の基底クラスArticleFormWidgetは、次のようになります。
class ArticleFormWidget(widgets.TableForm): def __init__(self): fields = self._create_fields() action = self._get_action() super(ArticleFormWidget, self).__init__('ArticleForm', fields=fields, action=action, submit_text=u'送信') def _create_fields(self): title_field = widgets.TextField('title', label=u'題名', attrs=dict(size='64'), validator=validators.UnicodeString(not_empty=True, max=64, messages={'empty': u'題名を入力してください。', 'tooLong': u'題名は、最大%(max)i文字です。'})) body_field = widgets.TextArea('body', label=u'本文', rows=16, cols=64, validator=validators.UnicodeString(not_empty=True, max=8192, messages={'empty': u'本文を入力してください。', 'tooLong': u'本文は、最大%(max)i文字です。'})) return [title_field, body_field]
ここで、widgets.TextFieldとwidgets.TextAreaに与えているvalidator引数が、ヴァリデータです。両方とも、validators.UnicodeStringを設定しています(他にどんなヴァリデータがあるかは、formencode.validators -- Validator/Converters for use with FormEncodeを参照してください)。validators.UnicodeStringに渡している引数のうち、not_emptyは空文字列を許可するかしないかを設定し、maxは最大長を設定します。messagesでは、どんなエラーが発生したらどんなメッセージを表示するかを設定します。'empty'は、何も入力されなかった場合のメッセージで、ここでは「題名を入力してください。」などにしています。'tooLong'は、最大文字数を越えたときのメッセージで、「題名は、最大%(max)i文字です。」などにしています。%(max)iには、最大文字数が入ります。
登録画面で使用するウィジェットは、次の通りです。_get_action関数で、フォームの送信先を返しています。クラスの定義がすんだら、インスタンスを生成しています。
class ArticleAddFormWidget(ArticleFormWidget): def __init__(self): super(ArticleAddFormWidget, self).__init__() def _get_action(self): return '/doadd' article_add_form_widget = ArticleAddFormWidget()
編集画面で使用するウィジェットは、次の通りです。_create_fields関数をオーバーライドして、編集対象のレコードのidを保持するhiddenフィールドを追加しています。_get_action関数で送信先を返し、クラスの定義後にインスタンスを生成するのは、ArticleAddFormWidgetクラスと同じです。
class ArticleEditFormWidget(ArticleFormWidget): def __init__(self): super(ArticleEditFormWidget, self).__init__() def _create_fields(self): fields = super(ArticleEditFormWidget, self)._create_fields() id_field = widgets.HiddenField('id') fields.append(id_field) return fields def _get_action(self): return '/doedit' article_edit_form_widget = ArticleEditFormWidget()
次に、これらのウィジェットを使用する箇所を作ります。まず、登録画面からです。
@expose(template="tgdiary.templates.add") @identity.require(identity.not_anonymous()) def add(self): return dict(article_add_form_widget=article_add_form_widget) @expose() @identity.require(identity.not_anonymous()) @error_handler(add) @validate(form=article_add_form_widget) def doadd(self, title, body): # TODO: Validate. article = Article(title=title, body=body, user=identity.current.user) self._redirect_to_date(identity.current.user.id, article.created)
フォームの送信先であるdoadd関数に、2つのデコレータを追加します。ひとつ目は、@error_handlerです。これで、エラー発生時の遷移先を設定します。ここでは、もういちど登録画面を表示したいので、add関数を指定しています。ふたつ目は、@validateです。これのform引数で、入力値検証に使用するウィジェットを指定します。
編集画面は、以下のようになります。
@expose(template="tgdiary.templates.edit") @identity.require(identity.not_anonymous()) def edit(self, id): # TODO: Validate id and user's id. article = Article.get(id) return dict(article=article, article_edit_form_widget=article_edit_form_widget) @expose() @identity.require(identity.not_anonymous()) @error_handler(edit) @validate(form=article_edit_form_widget) def doedit(self, id, title, body): # TODO: Validate id, title, body and user's id. # XXX: Better way? Transaction? article = Article.get(id) article.title = title article.body = body self._redirect_to_date(identity.current.user.id, article.created)
同様に、doedit関数に@error_handlerデコレータと@validateデコレータを追加しています。
ビューであるtgdiary/templates/add.kidとtgdiary/templates/edit.kidは、以下のように修正します。これは、単に変数の名前を変更したためであり、入力値検証とは関係ありません。tgdiary/templates/add.kidは、
${article_add_form_widget.display()}
であり、tgdiary/templates/edit.kidは、
${article_edit_form_widget.display(article)}
です。
以上により、入力値検証が完成しました。試しに、登録画面で何も入力せずに送信してみます。
メッセージの場所が気に入りませんが、見事にエラーメッセージが表示されました。最大文字数を越えて入力すると、以下のようになります。