【rails】form_forとform_tagの違い
form_forとform_tagの違いについて解説します。
教科書的には
- モデルに関連するときはform_for
- モデルに関連しないときはform_tag
みたいに言われています。
どういうことなのか具体的なコードを追いかけながら説明します。
メッセージ投稿機能
画面からメッセージを投稿する機能を想像してください。
流れは以下のような感じ
- 投稿画面を表示(newメソッド)
- ビューから入力(form_forとform_tagが記述されている部分)
- ストロングパラメータ
- モデルと使って保存(createメソッド)
- ビューに表示
まずはform_tagでメッセージ投稿機能を実現すると以下のようになります。
form_tagを使ったメッセージ投稿機能
newメソッド
def new end
メッセージ投稿画面を表示させるためのメソッド。これは「render "new"」が省略されているので、特に記述なしでいけます。
ビューのform_tag
<%= form_tag('/messages', method: :post) do %> <textarea name="content"></textarea> <input type="submit" value="SENT"> <% end %>
form_tagの引数にcreateのURLとメソッドにはpostを指定します。
ストロングパラメータ
def message_params params.permit(:content).merge(user_id: current_user.id) end
ストロングパラメータにはuser_idとcontentを指定します。
createメソッド
def create Message.create(message_params) end
ストロングパラメータを引数にcreateすることで、メッセージを保存します。
続いて、form_forによるメッセージ投稿機能です。
form_forを使ったメッセージ投稿機能
newメソッド
def new @message = Message.new end
form_forをメッセージ保存(create)で使いたい場合は、空のMessageモデルのインスタンスを渡す必要があります。
これはform_forが値を持ったインスタンスを渡されると更新(update)と判断してしまうので、newを使うことになります。
ビューのform_for
<%= form_for(@message) do |f| %> <%= f.text_area :content %> <%= button_tag type: "submit" do %>投稿する<% end %> <% end %>
form_tagではcreateのURLを指定しましたが、form_forでは@messageの状態でメソッドを自動で判断します。
- @messageが空 → create
- @massageに値あり → update
ストロングパラメータ
def message_params params.require(:message).permit(:content).merge(user_id: current_user.id) end
form_tagとの大きな違いはrequire(:message)の有無です。これはビューからコントローラに渡されるparamsの違いからこのような記述となっています。
createメソッドでbinding.pryした結果
- form_tagの場合 params {"content"=>"入力値", "user_id"=>1}
- form_forの場合 params "message"=>{"content"=>"入力値", "user_id"=>1}
form_forはmessageキーの値として、contentとuser_idが渡されているため、requireを記述する必要があります。
つまりrequire(モデル名).permit(カラム名)の形になります。
createメソッド
def create Message.create(message_params) end
createメソッドについてはform_tagと違いはありません。
form_forとform_tagの違いまとめ
結局以下のような違いがあるということです。
- newの書き方が異なる
- form_forとform_tagの引数が異なる
- ストロングパラメータの書き方が異なる
form_tag
def new end
form_for
def new
@message = Message.new
end
form_tag
<%= form_tag('/messages', method: :post) do %> <textarea name="content"></textarea> <input type="submit" value="SENT"> <% end %>
form_for
<%= form_for(@message) do |f| %>
<%= f.text_area :content %>
<%= button_tag type: "submit" do %>投稿する<% end %>
<% end %>
form_tag
def message_params params.permit(:content).merge(user_id: current_user.id) end
form_for
def message_params
params.require(:message).permit(:content).merge(user_id: current_user.id)
end
あとがき
form_forとform_tagの違いについて長々と説明してきましたが、この他にform_withというのもあります。
しかも、今後のRailsではform_forとform_tagは非推奨で、form_withに置き換えられる予定だそうです。
form_withについてはまたどこかで解説したいと思います。
参考
【Rails】form_for/form_tagの違い・使い分けをまとめた - Qiita
【Rails 5】(新) form_with と (旧) form_tag, form_for の違い - Qiita