form_with
がリクエストメソッドをどうやって判断してるのか気になったので調べた。
はじめに結論
モデルのインスタンスの#persisted?
メソッドがtrue
を返すならPATCH
、そうでなければPOST
。
書かないこと
-
form_with
の詳しい説明 -
form_for
とform_with
の違い
環境
- Ruby 2.5.0
- Rails 5.1.4
コードを追う
form_with
の動き
form_with
の定義は actionview/lib/action_view/helpers/form_helper.rbの745行目
この中でhtml_options_for_form_with
を呼んでhtml_options
(<form>
要素の属性値)を組み立ててる。
html_options = html_options_for_form_with(url, model, options)
html_options_for_form_with
の定義は同じファイルの1538行目
モデルインスタンスの#persisted?
がtrue
を返すとき、html_options[:method]
に:patch
が設定される。
html_options[:method] ||= :patch if model.respond_to?(:persisted?) && model.persisted?
#persisted?
が未定義だったりtrue
以外を返すときはhtml_options[:method]
が未設定になる。
その場合はどうなるか。
form_with
でhtml_options
を組み立てたあとの次の行、form_tag_with_bodyを呼ぶと内部で処理がform_tag_html > extra_tags_for_formと継続していく。
extra_tags_for_form
で、html_opitions[:method]
が未設定だったり空文字だったときは"post"が設定されるようになっている。
モデルの#persisted?
の定義
#persisted?
はActiveModelで定義されている。
APIドキュメントにあるように、デフォルトでfalse
を返すので必要に応じてオーバーライドする。
http://api.rubyonrails.org/classes/ActiveModel/Model.html
https://github.com/rails/rails/blob/v5.1.4/activemodel/lib/active_model/model.rb#L93
ActiveRecordではこれをオーバーライドしてレコードが既存のものかチェックを行っている。
https://github.com/rails/rails/blob/v5.1.4/activerecord/lib/active_record/persistence.rb#L98
まとめ
-
#persisted?
によってform_with
のリクエストメソッドが自動判別される - ActiveModelをincludeしたクラスを作るとき、
#persisted?
をオーバーライドするの忘れがちな気がするので注意する