niki12260714の日記

フリーランスのITエンジニアの呟き。

Active Modelを定義し、form_forを使用、validatesでエラー処理までの流れ

画面の入力項目が一つのテーブルだけではないというのはよくあるパターン。
そうすると、form_forが使えないので、form_tagを使うことになります。
が、そうするとvalidatesの処理とかセオリー通りにいかないし、form_forの方がBootstrapでごにょごにょするのに便利なわけですし。
なんで、こういう場合は「そのformで使うモデルを独自に定義し、それを使う」という技があります。
※あと、検索画面で「検索条件欄に入力が無かったらエラーにしたい」というのをvalidatesで定義しておきたいとか。

というわけで、独自モデルの定義。
railsが5以上の場合は、app/modelの下にconcernsというフォルダが出来ているので、ここを使うらしい?
※ファイルの配置位置は、私の解釈なんで、間違っていたらご指摘ください。
「item_add.rb」というファイルを追加し、以下を記述

【model】
class ItemAdd
 include ActiveModel::Model
 attr_accessor :col_a, :col_b, :col_c

 validates :col_a
  presence: true,
  length: {minimum: 1, maximum: 100}
end

ポイントは、赤字にした「include」のところで、ActiveModelを継承すること。
で、attr_accessorで、そのformで使う要素を列挙すること。
attr_accessorに書いた要素に対し、validatesを定義しておきます。
これは通常のModelに対する書き方と一緒。

で、この独自に定義したmodelをcontrollerでnewしてあげて、viewで使うわけです。

【controller】
def init
 #独自定義のモデルをロードする
 @add_form = ItemAdd.new()
end

【view】
<%= form_for @add_form, url: {controller: :xxx, action: :xxx} do |form| %>
 <%= form.label :col_a, "要素A" %>
 <%= form.text_field :col_a, id: :col_a%><br />
 <%= form.label :col_b, "要素B" %>
 <%= form.text_field :col_b, id: :col_b%><br />
 <%= form.label :col_c, "要素C" %>
 <%= form.text_field :col_c, id: :col_c%><br />
 <br />
 <%= form.submit "追加", class: :"btn btn-default" %>
<% end %>

これでview側で、form_forが使えます!
さて、これでsubmitされたら、validatesを通してチェックをしたいわけで、それはcontrollerでこんな感じで書きます。

【controller】
def xxx
 #入力値を元にモデルオブジェクトを生成
 add_form = ItemAdd.new(col_a: params[:item_add][:col_a], col_b: params[:item_add][:col_b], col_c: params[:item_add][:col_c])
 if !add_form.valid?
  #validatesでエラーになった場合の記述
  flash[:danger] = add_form.errors.full_messages[0]
 else
  #validatesに問題がない場合の記述
 end
end

ポイントは、赤字のnewしているところと、newしたオブジェクトに対し、valid?と判定をかけているところ。
newする時に、formでpostされたデータを入れてあげます。
その結果が正しいか(valid?)を確認し、falseが戻ってきたらエラーってことで、エラー処理を書けばよいです。
エラーのメッセージは、通常のmodelのvalidatesで戻されるのと同一です。

 

→エラーメッセージの日本語化について、追加の記事があります。

niki12260714.hatenablog.com