バリデーション(2)
バリデーションでエラーになったときにどうやって値が引き継がれるか調べてみたメモ。結論から言うとポストされてきた値を使ってポスト前のフォームをrenderするとよい。
samples_controller.rb
プロジェクトは前回のものと同じ。理解しやすくするため、コントローラのrespond_toからhtml対応部分のみ抜き出したものを作成。各メソッドの先頭にデバッグ出力を追加。
require 'pp' class SamplesController < ApplicationController # GET /samples def index pp 'index', params @samples = Sample.find(:all) end # GET /samples/1 def show pp 'show', params @sample = Sample.find(params[:id]) end # GET /samples/new def new pp 'new', params @sample = Sample.new end # GET /samples/1/edit def edit pp 'edit', params @sample = Sample.find(params[:id]) end # POST /samples def create pp 'create', params @sample = Sample.new(params[:sample]) if @sample.save flash[:notice] = 'Sample was successfully created.' redirect_to(@sample) else flash[:notice] = 'Sample was not created.' render :action => "new" end end # PUT /samples/1 def update pp 'update', params @sample = Sample.find(params[:id]) if @sample.update_attributes(params[:sample]) flash[:notice] = 'Sample was successfully updated.' redirect_to(@sample) else flash[:notice] = 'Sample was not updated.' render :action => "edit" end end # DELETE /samples/1 def destroy pp 'destroy', params @sample = Sample.find(params[:id]) @sample.destroy redirect_to(samples_url) end end
新規作成の流れは
- GET index # index.html.erb
- GET new # new.html.erb
- POST create
- (成功したとき) redirect_to(@sample) # GET show
- (失敗したとき) render(:action => "new") # new.html.erb
編集の流れは
- GET index # index.html.erb
- GET edit # edit.html.erb
- POST update
- (成功したとき) redirect_to(@sample) # GET show
- (失敗したとき) render(:action => "edit") # edit.html.erb
となっていて、失敗パターンで使われるテンプレートはポスト前と同じもの。scaffoldなのでこのあたりは全く意識しなくても動作するが、独自にコントローラを作成する場合はバリデーションを考慮してテンプレートに値を引き渡す必要がある。
ActionView::Helpers::FormHelper.form_for
テンプレート側として form_for の流れを確認しておく。
def form_for(record_or_name_or_array, *args, &proc) raise ArgumentError, "Missing block" unless block_given? # 引数からオプションHashを抜き出す options = args.extract_options! # 第1引数を解析 case record_or_name_or_array when String, Symbol # :sample と書かれた場合 object_name = record_or_name_or_array when Array # [:sample, @sample] と書かれた場合 object = record_or_name_or_array.last object_name = ActionController::RecordIdentifier.singular_class_name(object) apply_form_for_options!(record_or_name_or_array, options) args.unshift object else # @sample と書かれた場合 object = record_or_name_or_array object_name = ActionController::RecordIdentifier.singular_class_name(object) apply_form_for_options!([object], options) args.unshift object end # ブロックのコンテキストでフォームタグを追加 concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding) # フィールドの処理。引数は :sample, @sample, *args, option のようになる。 fields_for(object_name, *(args << options), &proc) concat('</form>', proc.binding) end
fields_for で ActionView::Helpers::FormTagHelper にあるメソッドが呼ばれることになる。このため、引数の順序が調整されている。
では、実際に fields_for でどのようなタグが生成されるか・・・特に独自コントローラでActiveRecordを使ってないような場合にどのようにするとよいのかを調べたかったが、残念。今日はここまで。