on Rails : depot : モデルを触る

お触りとかじゃありません.今回は,やっとこModelに手を加える段階に入ります.内容的には,6章3節.


相変わらず,とても簡単にやりたいことが出来ると言うか,かゆい所に手が届くと言うか,JSPって何だっけ.みたいな.一応,昨日の続きです.ちなみに,rhtmlやcssを弄ったりってのは楽しいけども,本質とは離れてる気もするので割愛.

入力の検証はモデルで

フォームから入力されたデータは,コンピュータ的には問題無くてもビジネス的には大問題だったりすることもしばしば.例えば,商品名が空だとすればSQLに怒られて済むけれども,価格がマイナスでもSQLは怒ってくれない.そういう入力のチェックはどこでするべきか?コントローラ?ビュー?どちらも否,答えはモデルだ.モデルは,データがデータベースに格納されるための,最後の門番になってくれる.

validates_*

それでは実際に,モデルを見てみる.app/models/product.rbを開く.昨日も書いたとおり,product.rbは殆ど何も書かれていない.

class Product < ActiveRecord::Base
end

だけ.コイツに色々追加していくのだな.AWDwRだと,とりあえず「入力があるかどうか」ってのを見るために,validates_presence_ofというメソッドを使っている.

  • validate:正当性を立証する,認証する
  • presence:存在すること

という意味ね.とりあえず,そいつを使ってこんなのを書いている.

validates_presence_of :title, :description, :image_url

これで,タイトル,概要,画像のURLが入力されているかを確認できる.しかも,入力されていなかったらビューにエラーまで出力してくれる!たった一行で!もちろんエラーが出るとデータベースは更新されない.すごいぞRails.ほかにもこのvalidates_*は色々あるようで,http://railsapi.masuidrive.jp/module/ActiveRecord::Validations::ClassMethodsにたくさん書いてある.また,にもtipsが書いているので,読んでおかないと.


そしてだ,このエラーメッセージ,標準だと英語だ.そりゃDHHは外人さんだもの,なにも弄らないで日本語のエラーメッセージが出力されるわけないよ.んでまーとりあえず,このvalidates_*に:messageというのを指定してやると,すんなり指定したエラーメッセージが出力される.結局,何も設定してないとデフォルトのエラーメッセージ(英語のそれ)が出力される訳だな.

validates_presence_of :title, :description, :image_url,
:message => "が入力されていません."

こうすると,例えばタイトルが書かれていないとき,「Title が入力されていません.」と出力される.

validate

ほかに入力のエラーをチェックする方法として,validateというメソッドをクラスに追加してやる方法がある.これも規約に沿った方法のようで,http://wota.jp/ac/?date=20050802によると,validateは新規,更新のときに実行されるということだ.また,外部から呼び出されないようにprotectedにしておく.

protected
def validate
    errors.add(:price, "は0より大きくなければなりません.") unless price.nil? || price > 0.0
end

こうすると,price(価格)が入力されていて,かつ0.0より小さいときにこのエラーが出力されることになる.

モデルだいたいできた

結局,validateion処理をある程度追加すると,こうなった.

class Product < ActiveRecord::Base
  validates_presence_of :title, :description, :image_url,
  :message => "が入力されていません."
  
  validates_numericality_of :price,
  :message => "は数字を入力してください."
  
  validates_uniqueness_of :title,
  :message => "が重複しています."
  
  validates_format_of :image_url,
  :with => %r{^http:.+\.(gif|jpg|png)$}i,
  :message => "はGIF,JPG,PNG画像のURLでなければなりません."

  protected
  def validate
    errors.add(:price, "は0より大きくなければなりません.") unless price.nil? || price > 0.0
  end
end

何だかんだでダラダラと長い気もするが,JSPでやることを考えると相当スマートに仕上がってるはずだ.しかも可読性が高く,わかり易い.

余談

なんだかんだでRailsも研究が進んでいるようで,グーグルさんに尋ねると答えが返ってくることが結構多い.たった2年やそこらしか経っていないのに.梅田望夫さん風に言うと,フリーウェイが敷かれていると言うか,とても勉強し易くなったはずだ.この調子で貪欲に吸収しよう.