またFormで難しい問題がでてきた。
Railsチュートリアル12章の課題の中に下記のような課題がでてきた。
form_forメソッドでは、なぜ@password_resetではなく:password_resetを使っているのでしょうか? 考えてみてください。
いまだにこのTutorialの解答がどこにいけば見られるかわかりません。
今回はform_forのモデルオブジェクト指定部分に:シンボルで指定する場合と@オブジェクトで指定する場合との違いを確認します。
前提条件
条件を揃えるために、呼び出すPATHはpassword_resets_urlとしています。
オブジェクトで指定する場合は確認した環境の都合上、@password_resetにUserモデルのオブジェクトを当て込んでいます。
シンボルで指定する場合
<%= form_for(:password_reset, url: password_resets_path) do |f| %>
<%= form_for(@password_reset, url: password_resets_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
上記から生成されるHTMLは下記
<form action="/password_resets" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="authenticity_token" value="hogehoge" />
<label for="password_reset_email">Email</label>
<input class="form-control" type="email" name="password_reset[email]" id="password_reset_email" />
<input type="submit" name="commit" value="Submit" class="btn btn-primary" data-disable-with="Submit" />
</form>
同じオブジェクト指定でもeditとnewとでは異なる
オブジェクトで指定した場合、Formのボタンを押したときに実行するアクションが指定されたオブジェクトの内容で異なります。
指定されたオブジェクトがUser.newで新規に作成されたレコードか、User.find(1)などで読み込まれた既存のレコードかでボタンを押したときの動作が変わります。
・指定されたオブジェクトが新規に作成されたレコードの場合、実行するアクションはPassword_resetsクラスのcreateアクションが実行されるためにPOSTメソッドで指定したURLが呼び出されます。
・指定されたオブジェクトが既存のレコードの場合、実行するアクションはPassword_resetsクラスのupdateアクションが実行されるためにPATCHで指定したURLを呼び出されます。
しかし、HTMLのFormからはPATCHメソッドの指定はできないため、""を使って擬似的にPATCHで送信するFormを作る必要があります。
newとeditそれぞれのアクションの内容は下記のようになっています。
動きを見たかっただけなので右辺と左辺の名称が違うのはほんと気にしないでください。
def new
@password_reset = User.new
end
def edit
@password_reset = User.find(1)
end
オブジェクトで指定する場合(新規レコード)
<%= form_for(@password_reset, url: password_resets_path) do |f| %>
<%= form_for(@password_reset, url: password_resets_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
上記から生成されるHTMLは下記
<form class="new_user" id="new_user" action="/password_resets" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="authenticity_token" value="hogehoge" />
<label for="user_email">Email</label>
<input class="form-control" type="email" name="user[email]" id="user_email" />
<input type="submit" name="commit" value="Submit" class="btn btn-primary" data-disable-with="Submit" />
</form>
オブジェクトで指定する場合(既存レコード)
<%= form_for(@password_reset, url: password_resets_path) do |f| %>
<%= form_for(@password_reset, url: password_resets_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
上記から生成されるHTMLは下記。なんかhiddenのフォームも作ってくれている
<form class="edit_user" id="edit_user_1" action="/password_resets" accept-charset="UTF-8" method="post">
<input name="utf8" type="hidden" value="✓" />
<input type="hidden" name="_method" value="patch" />
<input type="hidden" name="authenticity_token" value="hogehoge" />
<label for="user_email">Email</label>
<input class="form-control" type="email" value="example@railstutorial.org" name="user[email]" id="user_email" />
<input type="submit" name="commit" value="Submit" class="btn btn-primary" data-disable-with="Submit" />
</form>
結論
シンボル指定は余計なHTMLが生成されないが、Formを使ってオブジェクトを更新する作業には向いていない。
上記のコードはパスワードリセット用のメールを送るときのコードだが、パスワードリセットはオブジェクトの操作とか無いので、シンボル指定のほうが少ないコードでHTMLが生成される。
オブジェクトしては生成されるHTMLが若干多くなるが、オブジェクトを更新する際には新規か既存か、更新する対象のオブジェクトを良しなに勝手に推測してくれるので、同じフォームをパーシャルにしてnewとかeditとかに使い回すことができる。