My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
GAEO_Example02  
自動生成されたコードを修正してブログらしくする
example, memcache, validation
Updated Nov 17, 2009 by naoki.ts...@gmail.com

自動生成されたコードを修正してブログらしくする

自動生成されたコードでは、表示内容を含め、まともに使用できる状態ではないので、これを修正してもう少し使える状態に仕上げていきます。

注)ファイル名を示す場合は、全てプロジェクトルートからの相対パスで表記します。

デバッグのためにログを出力する

本来はユニットテストを利用する方がよいとは思いますが、手っ取り早くアプリの状況をログに表示したい場合には、標準のログパッケージが便利です。

次のようにログ出力パッケージを指定します。

import logging

次のように使用します。

logging.info("some message")
logging.info("%s" % someValue)
logging.error("some error message")

一覧表示はデータストアのキー値ではなく、タイトルと日付を表示する

生成された状態では、データストアのID(キー)の値が表示されていますが、これでは内容を把握できないので、タイトルを表示するように変更します。

プロジェクトの生成した「templates」ディレクトリ下の「index.html」の次の部分を変更します。

変更前

{% for r in result %}
            <li><a href="/example1/show/{{ r.key }}">{{ r.key }}</a></li>
{% endfor %}

変更後

{% for r in result %}
            <li><a href="/example1/show/{{ r.key }}">{{ r.title }}</a></li>
{% endfor %}

上記の result という変数は、コントローラのindexメソッド中では self.result と宣言され、データストアから取得したオブジェクトが格納されています。

現在は英語で表示されていますが、日本語で表示したい場合は、それぞれのテンプレートファイルを編集します。

対象のファイル群は、./application/templates/example2/ 以下にあります。

簡単なバリデーションを実装する

バリデーションの実装には標準パッケージに含まれているDjangoの djangoforms という便利なパッケージがあるのですが、GAEOでは使用ができないようです。(少なくとも作者はうまくいかなかった。情報をお持ちの方は是非、ご連絡ください)

というわけで、今回はGAEOのみで簡単なバリデーションを行ってみる事ことにします。

before_putメソッドにバリデーション処理を追加する

GAEO 0.3には before_put という、データストアにデータを格納する直前に呼ばれるコールバックメソッドがあります。 今回はこれを使用して、タイトルの文字列長が10文字を超えたらエラーとして扱うという簡単なバリデーションを行ってみます。

下の例では、 validates_size_of というRailsではおなじみのメソッド名を使って、タイトルフォームに入力された文字列長をチェックしています。

./application/model/example1.py

  def before_put(self):
    logging.info("### before_put")
    result = True
    result = self.validates_size_of()
    if result == False:
      return False
    logging.info("### Result:%s" % result)
    return result
    
  def validates_size_of(self):
    max_size = 10
    size = len(self.title.encode("UTF-8")) # 日本語は3文字としてカウントされる
    size = len(self.title) # 日本語は1文字としてカウントされる
    logging.info("******* size:%d" % size)
    if size <= max_size:
      self.errors = {} # 成功なら空
      return True
    else:
      self.errors.update({"title_too_long" : u"タイトルが長過ぎます"})
      raise NameError ('Title lengs is too long')
      return False
    pass

before_put()False を返すと、例外がスローされますので、コントローラこれを捕捉します。

./application/controller/example1.py

    def create(self):
        try:
          r = Example1(
              # Uncomment all required properties here.
              title = self.params.get('title', None),
              content = self.params.get('content', None),
              # フォームで入力せず、自動登録なのでコメントアウト
              # posted_at = self.params.get('posted_at', None),
              keyword = self.params.get('keyword', None),
          )
          for prop in Example1.properties():
              if prop in self.params:
                  setattr(r, prop, self.params.get(prop))
                
          result = r.put()
          logging.info("### datastore result is %s", result)
          self.flash['notice'] = u"追加しました。"
          self.redirect('/example1')
        except Exception, e:
          # 必須項目のフォームに値が無い場合、バリデーションがFalseを返したときに例外が発生する
          logging.info("*** ERROR => %s" % e)
          self.__set_attr()
          self.msg = u"入力に誤りがあります"
          self.render(template='new')

    ############################################
    # private method
    ############################################

    def __set_attr(self):
        """
        バリデーションでエラーがあった場合に、フォームに値を再セットするために使用する
        """
        self.title = self.params.get('title')
        self.keyword = self.params.get('keyword')
        self.content = self.params.get('content')
        pass

例外処理の中では、フォームに入力された内容をセットし、追加画面に移動する処理を行っています。 また、これらの処理はcreate()とupdate()で同じ処理を行うので、プライベートメソッドset_attr()としてまとめています。 追加画面への移動は、self.redirect()ではなく、 self.render() を使用している事に注意してください。

アトリビュートについて

update処理のようにデータストアから取得した値を、フォームに使用しているパラメータにセットする処理は、GAEOがscaffoldで出力した次のようなコードでもかまいませんが、

./application/controller/example1.py

    def update(self):
        try:
            r = Example1.get(self.params.get('id'))
            for prop in Example1.properties():
                if prop in self.params:
                    setattr(r, prop, self.params.get(prop))
            r.put()
            self.redirect('/example1/show/%s' % (r.key()))
        except Exception, e:
            .
            .
            .

GAEO 0.3から使用可能になった、 self. update_attributes() メソッドを利用する事もできます。このメソッドを使用した場合、次のようなコードになります。 r.put() が無い点に注意してください。 self. update_attributes() は自動で r.put() を呼び出します。

./application/controller/example1.py

    def update(self):
        try:
            r = Example1.get(self.params.get('id'))
            r.update_attributes(title=self.params.get('title'),
                                content=self.params.get('content')
                                )
            self.redirect('/example1/show/%s' % (r.key()))
        except Exception, e:
            .
            .
            .

エラーメッセージを表示する

入力ミスなどのエラーメッセージを表示したい場合は、 self.msg というインスタンス変数に 直接 エラーメッセージを代入し、正常に終了した場合のメッセージを表示したい場合には、 self.flash['notice'] にメッセージを代入します。 例外後のエラーメッセージを self.msg に直接代入している理由は、self.redirect()ではなく、self.render()を実行しているので、リダイレクト時と違い、 self.before_action() が呼び出されないためです。 なお、self.before_action() は0.3から利用可能です。self.flashは単なる辞書配列ですので、 notice の部分は error や warning などとすることができます。

./application/controller/example1.py

    def create(self):
        try:
            .
            .
            .
          self.flash['notice'] = u"追加しました。"
          self.redirect('/example1')
        except Exception, e:
          # 必須項目のフォームに値が無い場合、バリデーションでFalseを返したときに例外が発生する
          logging.info("*** ERROR => %s" % e)
          self.__set_attr()
          self.msg = u"入力に誤りがあります"
          self.render(template='new')

./application/controller/example1.py

    def before_action(self):
        notice = self.flash.get('notice', '')
        self.msg = notice
        pass

./application/templates/bash.html

    <div id="content_inner">
      {% if msg %}
        <div class="flash" style="color:#f00">
          <div class="notice">
            <p>{{ msg }}</p>
          </div>
        </div>
      {% else %}
      <!-- メッセージ無し -->
      {% endif %}
    </div>

これで、ブログに必要な一通りの処理が実装できました。

次回は、GAEのデータストアへのアクセスはCPU資源を大量に消費しますので、それを回避するために、キャッシュサービスを使用してみます。


Sign in to add a comment
Powered by Google Project Hosting