迷い人

日々勉強。勉強の先に何か見つかるといいなぁ

【rails】form_forとform_tagの違い

form_forとform_tagの違いについて解説します。

 

教科書的には

  • モデルに関連するときはform_for
  • モデルに関連しないときはform_tag

みたいに言われています。

 

どういうことなのか具体的なコードを追いかけながら説明します。

 

 

 

メッセージ投稿機能

画面からメッセージを投稿する機能を想像してください。

 

f:id:oyaoya1123:20191112004955p:plain

 

流れは以下のような感じ

 

  1. 投稿画面を表示(newメソッド)
  2. ビューから入力(form_forとform_tagが記述されている部分)
  3. ストロングパラメータ
  4. モデルと使って保存(createメソッド)
  5. ビューに表示

 

まずは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

form_tagとform_forの違い - Qiita

【Rails 5】(新) form_with と (旧) form_tag, form_for の違い - Qiita