注意: Rails 5.1以降はform_with
が推奨され、form_for
やform_tag
は非推奨になりました。
こんにちは、hachi8833です。
Rails 4および5のform_for
メソッドのAPIドキュメントをすべて翻訳いたしました。
Rails 3のform_for
メソッドのAPIドキュメント翻訳については以下をご覧ください。
概要
Rails 3、4、5のform_for
メソッドを最新の公式APIドキュメントでチェックしてみたところ、現時点でのRails 4とRails 5向けform_for
メソッドのAPIドキュメントは完全に一致していました。また、Rails 3とRails 4/5のドキュメントの差分もさほどありませんでした。
- APIドキュメント: form_for: Rails 4.2.8
- APIドキュメント: form_for: Rails 5.0.2
なお、form_for
とform_tag
は今後form_with
で一元的に利用できるようになるとのことです。
追伸: その後form_with
が公開されましたので、Rails 5.1以降はこちらを使うことをおすすめします。
form_for
(Rails 4およびRails 5)
#API呼び出し(Rails 3、4、5共通)
form_for(record, options = {}, &block)
- Rails 5のソース: actionview/lib/action_view/helpers/form_helper.rb
- Rails 4のソース: actionview/lib/action_view/helpers/form_helper.rb
特定のモデルオブジェクトの属性をユーザーが作成、更新できるフォームを作成します。
form_for
メソッドは、フォームの構成をRailsにどの程度まで自動的に推測させたいかに応じて、さまざまな方法で用いることができます。一般的なモデルオブジェクトの場合、扱いたいオブジェクトを表す文字列やシンボルを以下のようにform_for
に渡すことでフォームを作成できます。
<%= form_for :person do |f| %>
First name: <%= f.text_field :first_name %><br />
Last name : <%= f.text_field :last_name %><br />
Biography : <%= f.text_area :biography %><br />
Admin? : <%= f.check_box :admin %><br />
<%= f.submit %>
<% end %>
ブロックの変数f
は、form_for
に渡される:person
が指すモデルオブジェクトに関する情報を保持するFormBuilderオブジェクトです。FormBuilderで定義されているメソッドは、このモデルに紐付けられるフィールドの生成に使われます。
<%= f.text_field :first_name %>
たとえば、上のコードは以下のように展開されます。
<%= text_field :person, :first_name %>
展開されたコードからHTML<input>
タグが生成されます。このタグのname
属性はperson[first_name]
になります。これにより、フォームが送信されると、ユーザーの入力した値はコントローラでparams[:person][:first_name]
という形でアクセスできるようになります。
FormBuilderで生成されたフィールドは次のような性質を持ちます。たとえば:person
がたまたま@person
インスタンス変数でもある場合には、フォームを最初にユーザーが表示したとき(ユーザーが既存のレコードを編集中の場合など)に表示されるデフォルト値には、@person
の属性に対応する値が使われます。
form_for
の一番右(ブロックを除く)の引数はオプションハッシュであり、以下のオプションを利用できます。
:url
- フォームの送信先URLです。この引数は
url_for
やlink_to
に渡される値と同じ方法で表現できますので、名前付きルートを直接利用できます。上記のようにモデルを文字列やシンボルで表し、かつ:url
オプションを指定しない場合、フォームの送信先はデフォルトで現在のURLになります。なお、後述するform_for
のリソース指向の利用法では、URLを明示的に使用する必要がありません。 :namespace
- フォーム要素のid属性を一意にするための名前空間を指定します。名前空間の属性の前には、生成されたHTML idの後ろにアンダースコア
_
を付けたものが追加されます。 :method
- フォームの送信方法を指定します。
*get
、post
: 通常はこれを使います。
*patch
、put
、delete
: これらを使うと、post
でこのverbをシミュレートするために、_method
を付けた名前がフォームのinputに非表示で追加されます。 :authenticity_token
- フォームが本物であることを示すトークンです。
カスタムトークンを渡したい場合や、あえて:authenticity_token
にfalse
を指定してトークンを無効にする以外の目的では使わないでください。
config.action_view.embed_authenticity_token_in_remote_forms = false
を設定で指定することで、リモートフォームでのトークン埋め込みを省略できます。このオプションは、フォームでフラグメントキャッシュを行う場合に有用です。リモートフォームはトークンをmeta
タグで受け取るようになるので、JavaScriptが使えないブラウザをサポートしない場合にもトークンの埋め込みが不要になります。 :remote
true
を指定すると、送信の挙動をUnobtrusive JavaScript(控えめなJavaScript)で制御できるようになります。デフォルトではAjax送信になります。:enforce_utf8
false
を指定すると、utf8
という名前の非表示inputを出力しなくなります。:html
- フォームのタグにHTML属性を追加するのに使います。
form_for
は排他的なスコープを作成しないことにご注意ください。排他的なスコープの作成は、単独のFormHelperのメソッドとFormTagHelperのメソッドを両方使えば可能です。次の例をご覧ください。
<%= form_for :person do |f| %>
First name: <%= f.text_field :first_name %>
Last name : <%= f.text_field :last_name %>
Biography : <%= text_area :person, :biography %>
Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
<%= f.submit %>
<% end %>
上の方法は、オブジェクトをベースとする設計になっているFormOptionHelperやDateHelperのメソッドでも使えます(FormOptionHelper#collection_select
やActionView::Helpers::DateHelper#datetime_select)。
form_for
をモデルオブジェクトで使う
上の例では、作成または編集されるオブジェクトはシンボルとして表現されて#form_for
に渡されていますが、文字列でも同様に渡せます。また、前述のとおり#form_for
にはモデルオブジェクト自身も渡せます。たとえば、フォームで編集したい既存のレコードが@post
である場合、以下のようにしてフォームを作成できます。
<%= form_for @post do |f| %>
...
<% end %>
このようにして作成したフォームの挙動は、前述の方法とほぼ同じですが、若干異なる点があります。
第一に、フォーム内の1つまたは複数のinput要素(これらはparams
ハッシュのキーになります)に付けられる名前のプレフィックスは、そのオブジェクトのクラスから引用されます。たとえば、Post
というクラスであればparams[:post]
となります。:as
オプションを使うと、たとえば以下のようにプレフィックスを上書きできます。
<%= form_for(@person, as: :client) do |f| %>
...
<% end %>
上のコードではparams[:client]
を得られます。
第二に、フォームで最初に表示されるフィールドの値には、form_for
に渡されるオブジェクトの属性が使われます。この挙動は、オブジェクトがインスタンス変数であるかどうかにかかわらず同じです。したがって、たとえばpost
という「ローカル」変数があり、それが既存のレコードを表す場合は次のようになります。
<%= form_for post do |f| %>
...
<% end %>
上のコードで最初に表示されるフォームには、ローカル変数post
の属性の現在の値が反映されます。
リソース指向のスタイル
ここまでのコード例には明示的に書かれていませんでしたが、実際には:url
オプションでフォームの送信先を指定する必要があります。ただし、#form_for
に渡されるレコードがリソースの場合は、フォームの送信先はもっと簡単に指定できます。
たとえば、対応するRESTfulなルーティングのセットがある(つまりconfig/routes.rbでリソースとして定義されている)場合が該当します。
Railsではこのような場合、最適なURLを次のようにレコード自身から推測します。
<%= form_for @post do |f| %>
...
<% end %>
上のコードは、以下のようなコードと同等です。
<%= form_for @post,
as: :post,
url: post_path(@post),
method: :patch,
html: { class: "edit_post", id: "edit_post_45" } do |f| %>
...
<% end %>
新規レコードの場合は以下のように書けます。
<%= form_for(Post.new) do |f| %>
...
<% end %>
上のコードは、以下のようなコードと同等です。
<%= form_for @post,
as: :post,
url: posts_path,
html: { class: "new_post", id: "new_post" } do |f| %>
...
<% end %>
これらは次のように上書きできます。
<%= form_for(@post, url: super_posts_path) do |f| %>
...
<% end %>
送信フォーマットも次のように指定できます。
<%= form_for(@post, format: :json) do |f| %>
...
<% end %>
admin_post_url:
のような名前空間化されたルーティングの場合は次のように書けます。
<%= form_for([:admin, @post]) do |f| %>
...
<% end %>
リソースに何らかの関連付けが定義されている場合は、ルーティングが正しく設定されているdocument
に次のようにcomment
を追加できます。
<%= form_for([@document, @comment]) do |f| %>
...
<% end %>
なお、上のコードでは@document = Document.find(params[:id])
および@comment = Comment.new
が設定されていることが前提です。
メソッドを設定する
オプションハッシュ内に次のように書くと、HTTP verbの完全な配列をフォームに渡して強制的に使うように設定できます。
method: (:get|:post|:patch|:put|:delete)
こうすると、GET
やPOST
(これらはHTMLフォームでネイティブでサポートされます)でないverbが使われた場合に、フォームでPOST
が設定され、_method
と呼ばれる非表示のinputによって、サーバーが解釈できる期待どおりのverbが実行されます。
「控えめなJavaScript」によるAjax
オプションハッシュで以下を指定すると、いわゆる「控えめな(unobtrusive)JavaScript」のドライバでフォームの挙動を変更できます。
remote: true
このオプションを指定した場合のデフォルトの挙動では、通常のPOST
ではなくXMLHttpRequest
がバックグラウンドで動作することが期待されますが、最終的な動作はJavaScriptドライバの実装によって決定されます。
仮にフォームのさまざまな要素をJavaScriptによってシリアライズした場合でも、フォームを受信する側(つまりサーバー)から見ると通常の送信と同じ挙動になり、すべての要素をparams
で受け取れます。
次のコード例をご覧ください。
<%= form_for(@post, remote: true) do |f| %>
...
<% end %>
上のERBによって次のHTMLが生成されます。
<form action='http://www.example.com' method='post' data-remote='true'>
<input name='_method' type='hidden' value='patch' />
...
</form>
HTMLオプションを設定する
data-
属性はdata:
ハッシュで直接渡せますが、他のすべてのHTMLオプションについては次のようにhtml:
ハッシュの中に置く必要があります。
<%= form_for(@post,
data: { behavior: "autosave" },
html: { name: "go" }) do |f| %>
...
<% end %>
上のコードによって以下のHTMLが生成されます。
<form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
<input name='_method' type='hidden' value='patch' />
...
</form>
非表示のモデルidを出力しないようにする
#form_for
メソッドを使うと、自動的にモデルidが隠しフィールドとしてフォームに含まれます。このモデルidは、フォームデータとそれに関連付けられているモデルとの関連を保つために使われます。
ORMシステムによってはネストしたモデルでこうしたidを使わないものもあるので、その場合は次のようにinclude_id: false
を指定することで隠しフィールドのモデルidを出力しないようにできます。
<%= form_for(@post) do |f| %>
<%= f.fields_for(:comments, include_id: false) do |cf| %>
...
<% end %>
<% end %>
上の例では、NoSQLデータベースにPost
というモデルがひとつと、それに一対多で関連付けられるComment
というモデルが保存されています。:comments
には主キーはありません。
フォームビルダをカスタマイズする
FormBuilder
クラスをカスタマイズしてフォームをビルドすることもできます。カスタマイズするには、FormBuilder
を継承してサブクラスを作り、必要なヘルパーメソッドを定義またはオーバーライドします。
次のコード例では、フォームのinputにラベルを自動追加するヘルパーを作成済みであることが前提です。
<%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.text_area :biography %>
<%= f.check_box :admin %>
<%= f.submit %>
<% end %>
上のようにコードを書いてから、次のコードを書きます。
<%= render f %>
これにより、people/_labelling_form
というテンプレートを使ってレンダリング(=HTML生成)され、ローカル変数f
が参照するフォームビルダの名前はlabelling_form
となります。
特に指定しない限り、カスタムのFormBuilder
クラスは、ネストした#fields_for
呼び出しのオプションと自動的にマージされます。
上のようなコードを別のヘルパーにも含めておきたい場合は、以下のように書けます。
def labelled_form_for(record_or_name_or_array, *args, &block)
options = args.extract_options!
form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
end
モデルのインスタンスにフォームをアタッチする必要がない場合は、ActionView::Helpers::FormTagHelper#form_tagを参照してください。
外部リソースを使うフォーム
外部リソース(認証トークンの設定が必要な場合や、認証なしでフォームだけ表示したい場合などを含む)を扱うフォームをビルドする場合を考えてみましょう。何らかの支払い用ゲートウェイ番号にデータを送信しなければならず、フィールドの種類にも制限があるとします。
必要な認証トークンを渡すには、:authenticity_token
オプションを使います。
<%= form_for @invoice,
url: external_url,
authenticity_token: 'external_token' do |f| %>
...
<% end %>
認証トークンを一切出力したくない場合は、単にfalse
を渡します。
<%= form_for @invoice,
url: external_url,
authenticity_token: false do |f| %>
...
<% end %>