Webアプリケーションのフォームは、ユーザー入力を扱うための重要なインターフェイスです。しかしフォームのマークアップは、フォームのコントロールの命名法や大量の属性を扱わなければならず、作成もメンテナンスも退屈な作業になりがちです。そこでRailsでは、フォームのマークアップを生成するビューヘルパーを提供し、こうした煩雑な作業を行わずに済むようにしました。しかし現実にはさまざまなユースケースがあるため、開発者はこれらを実際に使う前に、これらのよく似たヘルパーメソッド群にどのような違いがあるのかをすべて把握しておく必要があります。
このガイドの内容:
authenticity_token
を設定する方法このガイドはフォームヘルパーとその引数について網羅的に説明するものではありません。完全なリファレンスについてはRails APIドキュメントを参照してください。
最も基本的なフォームヘルパーはform_with
です。
<%= form_with do |form| %> Form contents <% end %>
上のようにform_with
を引数なしで呼び出すと、<form>
タグを生成します。このフォームを現在のページに送信するときにHTTP POSTメソッドが使われます。たとえば現在のページがhomeページの場合、以下のようなHTMLが生成されます。
<form accept-charset="UTF-8" action="/" method="post"> <input name="authenticity_token" type="hidden" value="J7CBxfHalt49OSHp27hblqK20c9PgwJ108nDHX/8Cts=" /> Form contents </form>
上のHTMLでは、input
要素にtype=hidden
が指定されていることがわかります。GET以外のフォームはinput
がないと正常に送信できないので、このinput
は重要です。
authenticity_token
という名前の隠しinput
要素は、クロスサイトリクエストフォージェリ(CSRF)保護 と呼ばれるRailsのセキュリティ機能です。フォームヘルパーは、GET以外のフォームでこのトークンを生成します(CSRF保護が有効な場合)。詳しくはセキュリティガイドを参照してください。
検索フォームはWebでよく使われています。検索フォームには以下のものが含まれています。
この検索フォームの作成には、以下のように、form_with
とその中で生成されるフォームビルダーオブジェクトを使います。
<%= form_with url: "/search", method: :get do |form| %> <%= form.label :query, "Search for:" %> <%= form.text_field :query %> <%= form.submit "Search" %> <% end %>
上のコードから以下のHTMLが生成されます。
<form action="/search" method="get" accept-charset="UTF-8" > <label for="query">Search for:</label> <input id="query" name="query" type="text" /> <input name="commit" type="submit" value="Search" data-disable-with="Search" /> </form>
form_with
にurl: my_specified_path
を渡すと、リクエストの送信先をフォームで指定できるようになります。しかし後述するように、フォームにActive Recordオブジェクトを渡すことも可能です。
すべてのフォーム入力のid
属性は、name
属性から生成されます(上の例では"query")。このid
属性は、CSSでスタイルを設定するときや、JavaScriptでフォームを操作するときに非常に便利です。
GETメソッドは検索フォームでお使いください。こうすることで検索クエリがURLの一部に含まれ、ユーザーが検索結果をブックマークしておけば、後で同じ検索をブックマークから実行できるようになります。Railsでは基本的に、常にアクションに対応する適切なHTTPメソッド(verb)を選んでください(訳注: セキュリティガイドにも記載されているように、たとえば更新フォームでGETメソッドを使うと重大なセキュリティホールが生じる可能性があります)。
form_with
で生成するフォームビルダーオブジェクトは、テキストフィールド/チェックボックス/ラジオボタンなどのフォーム要素を生成するヘルパーメソッドを多数提供します。
これらのヘルパーメソッドに渡す第1パラメータには、常にinputの名前を指定します。
フォームが送信されると、フォームデータとともにこの名前も渡され、ユーザーがフィールドに入力した値とともにコントローラのparams
に送られます。たとえばフォームに<%= form.text_field :query %>
があると、コントローラ側でparams[:query]
と書くことでこのフィールドの値を取り出せるようになります。
Railsでは、inputに名前を与えるときに特定の規約を利用します。これにより、配列やハッシュのような「非スカラー値」のパラメータをフォームから送信できるようになり、コントローラでもparams
にアクセス可能になりますこれらの命名規約について詳しくは、本ガイドで後述する「パラメータの命名ルールを理解する」を参照してください。これらのヘルパーの具体的な利用法について詳しくはActionView::Helpers::FormTagHelper
APIドキュメントを参照してください。
チェックボックスはフォームコントロールの一種で、ユーザーが選択肢の項目をオン/オフできるようにします。
<%= form.check_box :pet_dog %> <%= form.label :pet_dog, "I own a dog" %> <%= form.check_box :pet_cat %> <%= form.label :pet_cat, "I own a cat" %>
上のコードによって以下が生成されます。
<input type="checkbox" id="pet_dog" name="pet_dog" value="1" /> <label for="pet_dog">I own a dog</label> <input type="checkbox" id="pet_cat" name="pet_cat" value="1" /> <label for="pet_cat">I own a cat</label>
check_box
の第1パラメータはinputの名前(name
)です。チェックボックスの値 (params
に表示される値) は、オプションで第3パラメータと第4パラメータで指定できます。詳しくはAPIドキュメントを参照してください。
チェックボックスと同様、ラジオボタンもユーザーが一連の選択肢から項目を選択できますが、一度に1つの項目しか選択できない排他的な動作が特徴です。
<%= form.radio_button :age, "child" %> <%= form.label :age_child, "I am younger than 21" %> <%= form.radio_button :age, "adult" %> <%= form.label :age_adult, "I am over 21" %>
出力は以下のようになります。
<input type="radio" id="age_child" name="age" value="child" /> <label for="age_child">I am younger than 21</label> <input type="radio" id="age_adult" name="age" value="adult" /> <label for="age_adult">I am over 21</label>
radio_button
の第2パラメータは、inputの値(value
)です。2つのラジオボタン項目は同じ名前('age')を共有しているので、ユーザーは一方の値だけを選択できます。これにより、params[:age]
の値は"child"と"adult"のいずれかになります。
チェックボックスとラジオボタンには必ずラベルを表示してください。ラベルを表示することで、そのオプションとラベルの名前が関連付けられるだけでなく、ラベルの部分もクリック可能になるのでユーザーの操作性が向上します。
これまで紹介した他にも、以下の「テキストエリア」「隠しフィールド」「パスワードフィールド」「数値フィールド」「日付時刻フィールド」など多くのフォームコントロールを利用できます。
<%= form.text_area :message, size: "70x5" %> <%= form.hidden_field :parent_id, value: "foo" %> <%= form.password_field :password %> <%= form.number_field :price, in: 1.0..20.0, step: 0.5 %> <%= form.range_field :discount, in: 1..100 %> <%= form.date_field :born_on %> <%= form.time_field :started_at %> <%= form.datetime_local_field :graduation_day %> <%= form.month_field :birthday_month %> <%= form.week_field :birthday_week %> <%= form.search_field :name %> <%= form.email_field :address %> <%= form.telephone_field :phone %> <%= form.url_field :homepage %> <%= form.color_field :favorite_color %>
上の出力は以下のようになります。
<textarea name="message" id="message" cols="70" rows="5"></textarea> <input type="hidden" name="parent_id" id="parent_id" value="foo" /> <input type="password" name="password" id="password" /> <input type="number" name="price" id="price" step="0.5" min="1.0" max="20.0" /> <input type="range" name="discount" id="discount" min="1" max="100" /> <input type="date" name="born_on" id="born_on" /> <input type="time" name="started_at" id="started_at" /> <input type="datetime-local" name="graduation_day" id="graduation_day" /> <input type="month" name="birthday_month" id="birthday_month" /> <input type="week" name="birthday_week" id="birthday_week" /> <input type="search" name="name" id="name" /> <input type="email" name="address" id="address" /> <input type="tel" name="phone" id="phone" /> <input type="url" name="homepage" id="homepage" /> <input type="color" name="favorite_color" id="favorite_color" value="#000000" />
隠しinputはユーザーには表示されず、種類を問わず事前に与えられた値を保持します。隠しフィールドに含まれている値はJavaScriptで変更できます。
「検索」「電話番号」「日付」「時刻」「色」「日時」「ローカル日時」「月」「週」「URL」「メールアドレス」「数値」「範囲」フィールドは、HTML5から利用可能になったコントロールです。 これらのフィールドを古いブラウザでも同じように扱いたい場合は、CSSやJavaScriptを用いるHTML5ポリフィルが必要になるでしょう。 古いブラウザでHTML5に対応する方法は山ほどありますが、現時点で代表的なものはModernizrでしょう。これらは、HTML5の新機能が使われていることを検出すると、機能を追加するためのシンプルな方法を提供します。
パスワード入力フィールドを使っている場合は、入力されたパスワードをRailsのログに残さないようにするとよいでしょう。方法についてはセキュリティガイドを参照してください。
form_with
の:model
引数を使うと、フォームビルダーオブジェクトをモデルオブジェクトに紐付けできるようになります。つまり、フォームはそのモデルオブジェクトを対象とし、そのモデルオブジェクトの値がフォームのフィールドに自動入力されるようになります。
たとえば、以下のような@article
というモデルオブジェクトがあるとします。
@article = Article.find(42) # => #<Article id: 42, title: "My Title", body: "My Body">
以下はそのフォームです。
<%= form_with model: @article do |form| %> <%= form.text_field :title %> <%= form.text_area :body, size: "60x10" %> <%= form.submit %> <% end %>
HTML出力は以下のようになります。
<form action="/articles/42" method="post" accept-charset="UTF-8" > <input name="authenticity_token" type="hidden" value="..." /> <input type="text" name="article[title]" id="article_title" value="My Title" /> <textarea name="article[body]" id="article_body" cols="60" rows="10"> My Body </textarea> <input type="submit" name="commit" value="Update Article" data-disable-with="Update Article"> </form>
上では以下のようにさまざまなことが行われています。
action
には、@article
に適した値が自動入力されている。@article
にある値が自動入力されている。article[...]
という形でスコープされている。これは、params[:article]
がすべてのフィールドの値を含むハッシュになるということです。input名について詳しくは、本ガイドで後述する「パラメータの命名ルールを理解する」を参照してください。通常、inputにはモデルの属性が反映されます。しかしこれは必須ではありません。フォームに他の情報を含めたい場合は、属性と同じようにフォームに含めればparams[:article][:my_nifty_non_attribute_input]
でアクセスできるようになります。
fields_for
ヘルパーfields_for
ヘルパーを使えば、<form>
タグを実際に作成せずにフォームとオブジェクトを同様に紐付けできます。これは、同じフォームで別のモデルオブジェクトのフィールドをレンダリングしたいときに便利です。たとえば、Person
モデルと、それに関連付けられるContactDetail
モデルがある場合は、以下のようにフォームを作成できます。
<%= form_with model: @person do |person_form| %> <%= person_form.text_field :name %> <%= fields_for :contact_detail, @person.contact_detail do |contact_detail_form| %> <%= contact_detail_form.text_field :phone_number %> <% end %> <% end %>
上のコードから以下のHTML出力が得られます。
<form action="/people" accept-charset="UTF-8" method="post"> <input type="hidden" name="authenticity_token" value="bL13x72pldyDD8bgtkjKQakJCpd4A8JdXGbfksxBDHdf1uC0kCMqe2tvVdUYfidJt0fj3ihC4NxiVHv8GVYxJA==" /> <input type="text" name="person[name]" id="person_name" /> <input type="text" name="contact_detail[phone_number]" id="contact_detail_phone_number" /> </form>
fields_for
で生成されるオブジェクトは、form_with
で生成されるのと同様のフォームビルダーです。
これでアプリケーションのユーザーがArticleモデルを直接操作できるようになりました。Rails開発では、次にこれをルーティングでリソース として宣言するのがベストプラクティスです。
resources :articles
リソースを宣言すると、他にも多くの設定が自動的に行われます。リソースの設定方法について詳しくは、Railsルーティングガイドを参照してください。
RESTfulなリソースを扱っている場合、レコード識別(record identification)を使うとform_with
の呼び出しがはるかに簡単になります。これは、モデルのインスタンスを渡すだけで、後はRailsがそこからモデル名など必要なものを取り出して処理してくれるというものです。以下の例では、長いバージョンと短いバージョンのどちらも同じ出力を得られます。
## 新しい記事の作成 # 長いバージョン form_with(model: @article, url: articles_path) # 短いバージョン(レコード識別を利用) form_with(model: @article) ## 既存の記事の編集 # 長いバージョン form_with(model: @article, url: article_path(@article), method: "patch") # 短いバージョン(レコード識別を利用) form_with(model: @article)
短い方のform_with
呼び出しは、レコードの作成・編集のどちらでもまったく同じです。これがどれほど便利であるかおわかりいただけると思います。レコード識別は、レコードが新しいかどうかをrecord.persisted?
で識別します。さらに送信用の正しいパスを選択し、オブジェクトのクラスに基づいた名前も選択してくれます。
単数形リソースを使う場合は、form_with
が機能するために以下のようにresource
とresolve
を呼び出す必要があります。
resource :geocoder resolve('Geocoder') { [:geocoder] }
モデルで単一テーブル継承(STI: single-table inheritance)を使っている場合、親クラスがリソースを宣言されていてもサブクラスでレコード識別を利用できません。その場合は:url
と:scope
(モデル名)を明示的に指定する必要があります。
名前空間付きのルーティングを作成してある場合、form_with
でもこれを利用した簡潔な表記を利用できます。アプリケーションのルーティングでadmin名前空間が設定されているとします。
form_with model: [:admin, @article]
上のコードはそれによって、admin名前空間内にあるArticlesController
に送信するフォームを作成します(たとえば更新の場合はadmin_article_path(@article)
に送信されます)。名前空間の階層が複数ある場合にも同様の文法が使えます。
form_with model: [:admin, :management, @article]
Railsのルーティングシステムおよび関連するルールについて詳しくはルーティングガイドを参照してください。
Railsのフレームワークは、開発者がアプリケーションをRESTfulな設計で構築することを推奨しています。すなわち、開発者はGETやPOSTリクエストだけでなく、PATCHやDELETEリクエストを多数作成・送信することになります。しかし、現実のブラウザの多くはフォーム送信時にGETとPOST以外のHTTPメソッドをサポートしていません。
そこでRailsでは、POSTメソッド上でこれらのメソッドをエミュレートすることによってこの問題を解決しています。具体的には、"_method"
という名前の隠し入力をフォームに用意し、使いたいメソッドをここで指定します。
form_with(url: search_path, method: "patch")
上のコードから以下の出力が得られます。
<form accept-charset="UTF-8" action="/search" method="post"> <input name="_method" type="hidden" value="patch" /> <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /> <!-- ... --> </form>
Railsは、POSTされたデータを解析する際にこの特殊な_method
パラメータをチェックし、ここで指定されているメソッド(この場合はPATCH)があたかも実際にHTTPメソッドとして指定されたかのように振る舞います。
formmethod:
キーワードを指定すると、フォームをレンダリングするときに送信ボタンが指定のmethod
属性をオーバーライドできるようになります。
<%= form_with url: "/posts/1", method: :patch do |form| %> <%= form.button "Delete", formmethod: :delete, data: { confirm: "Are you sure?" } %> <%= form.button "Update" %> <% end %>
<form>
要素の場合と同様、ほとんどのブラウザはformmethod
で宣言されるGETとPOST以外のフォームメソッドをサポートしていません。
Railsでは、POSTメソッド上でこれらのメソッドをエミュレートすることによってこの問題を解決しています。具体的には、formmethod
、value
、name
属性を組み合わせることでエミュレートします。
<form accept-charset="UTF-8" action="/posts/1" method="post"> <input name="_method" type="hidden" value="patch" /> <input name="authenticity_token" type="hidden" value="f755bb0ed134b76c432144748a6d4b7a7ddf2b71" /> <!-- ... --> <button type="submit" formmethod="post" name="_method" value="delete" data-confirm="Are you sure?">Delete</button> <button type="submit" name="button">Update</button> </form>
HTMLでセレクトボックスを作成するには大量のマークアップを書かなくてはなりません(選択するオプションごとに1つの<option>
要素が対応します)。そこでRailsでは、こうした作業を軽減するヘルパーメソッドを提供しています。
たとえば、ユーザーに選択して欲しい都市名のリストがあるとします。select
ヘルパーを使うと以下のようにセレクトボックスを作成できます。
<%= form.select :city, ["Berlin", "Chicago", "Madrid"] %>
上のコードで以下のHTMLが出力されます。
<select name="city" id="city"> <option value="Berlin">Berlin</option> <option value="Chicago">Chicago</option> <option value="Madrid">Madrid</option> </select>
以下のようにセレクトボックスの表示名と別の<option>
値を指定することも可能です。
<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]] %>
上のコードで以下のHTMLが出力されます。
<select name="city" id="city"> <option value="BE">Berlin</option> <option value="CHI">Chicago</option> <option value="MD">Madrid</option> </select>
こうすることで、ユーザーには完全な都市名が表示されますが、params[:city]
は"BE"
、"CHI"
、"MD"
のいずれかの値になります。
最後に、:selected
引数を使うとセレクトボックスのデフォルト値も指定できます。
<%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]], selected: "CHI" %>
上のコードで以下のHTMLが出力されます。
<select name="city" id="city"> <option value="BE">Berlin</option> <option value="CHI" selected="selected">Chicago</option> <option value="MD">Madrid</option> </select>
場合によっては、関連するオプションをグループ化してユーザーエクスペリエンスを向上させたいことがあります。これは、以下のようにselect
にHash
(または同等のArray
)を渡すことで行なえます。
<%= form.select :city, { "Europe" => [ ["Berlin", "BE"], ["Madrid", "MD"] ], "North America" => [ ["Chicago", "CHI"] ], }, selected: "CHI" %>
上のコードで以下のHTMLが出力されます。
<select name="city" id="city"> <optgroup label="Europe"> <option value="BE">Berlin</option> <option value="MD">Madrid</option> </optgroup> <optgroup label="North America"> <option value="CHI" selected="selected">Chicago</option> </optgroup> </select>
セレクトボックスも、他のフォームコントロールと同様にモデル属性に紐付け可能です。たとえば、以下の@person
というモデルオブジェクトがあるとします。
@person = Person.new(city: "MD")
以下はそのフォームです。
<%= form_with model: @person do |form| %> <%= form.select :city, [["Berlin", "BE"], ["Chicago", "CHI"], ["Madrid", "MD"]] %> <% end %>
セレクトボックスのHTML出力は以下のようになります。
<select name="person[city]" id="person_city"> <option value="BE">Berlin</option> <option value="CHI">Chicago</option> <option value="MD" selected="selected">Madrid</option> </select>
適切なオプションにselected="selected"
が自動的に追加されている点にご注目ください。このセレクトボックスはモデルに紐付けられているので、:selected
引数を指定する必要はありません。
Railsでタイムゾーンをサポートするために、ユーザーが今どのタイムゾーンにいるのかを何らかの形でユーザーに尋ねなければなりません。そのためには、collection_select
ヘルパーを使って、事前定義済みのActiveSupport::TimeZone
オブジェクトのリストからセレクトボックスを作成する必要がありますが、以下のようにその機能を既に持っているtime_zone_select
ヘルパーを使えば簡単にできます。
<%= form.time_zone_select :time_zone %>
以前のRailsには国を選択するcountry_select
ヘルパーがありましたが、この機能はcountry_selectプラグインに切り出されました。
HTML5標準の日付/時刻入力フィールドを生成するヘルパーを使いたくない場合は、Railsにある別の日付/時刻ヘルパーを使うこともできます。Railsの日付/時刻ヘルパーは、年/月/日などの一時コンポーネントごとにセレクトボックスをレンダリングします。たとえば、以下のような@person
というモデルオブジェクトがあるとします。
@person = Person.new(birth_date: Date.new(1995, 12, 21))
以下はそのフォームです。
<%= form_with model: @person do |form| %> <%= form.date_select :birth_date %> <% end %>
セレクトボックスのHTML出力は以下のようになります。
<select name="person[birth_date(1i)]" id="person_birth_date_1i"> <option value="1990">1990</option> <option value="1991">1991</option> <option value="1992">1992</option> <option value="1993">1993</option> <option value="1994">1994</option> <option value="1995" selected="selected">1995</option> <option value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> <option value="1999">1999</option> <option value="2000">2000</option> </select> <select name="person[birth_date(2i)]" id="person_birth_date_2i"> <option value="1">January</option> <option value="2">February</option> <option value="3">March</option> <option value="4">April</option> <option value="5">May</option> <option value="6">June</option> <option value="7">July</option> <option value="8">August</option> <option value="9">September</option> <option value="10">October</option> <option value="11">November</option> <option value="12" selected="selected">December</option> </select> <select name="person[birth_date(3i)]" id="person_birth_date_3i"> <option value="1">1</option> ... <option value="21" selected="selected">21</option> ... <option value="31">31</option> </select>
フォームが送信されたときのparams
ハッシュには、完全な日付を含む単一の値が存在しない点にご注目ください。代わりに、"birth_date(1i)"
のような特殊な名前を持つ複数の値が存在します。Active Recordは、モデル属性の宣言された型に基づいて、これらの特殊な名前を持つ値を完全な日付や時刻として組み立てる方法を知っています。つまり、フォームで完全な日付を表す1個のフィールを使う場合と同じように、params[:person]
をPerson.new
やPerson#update
などに渡せるということです。
Railsでは、date_select
ヘルパーの他にtime_select
ヘルパーやdatetime_select
ヘルパーも提供しています。
Railsでは、個別の一時コンポーネント向けのセレクトボックスをレンダリングするヘルパーとしてselect_year
、select_month
、select_day
、select_hour
、select_minute
、select_second
も提供しています。
これらのヘルパーは「素の」メソッドなので、フォームビルダーのインスタンスでは呼び出されません。たとえば以下のようにselect_year
ヘルパーを使うとします。
<%= select_year 1999, prefix: "party" %>
セレクトボックスのHTML出力は以下のようになります。
<select name="party[year]" id="party_year"> <option value="1994">1994</option> <option value="1995">1995</option> <option value="1996">1996</option> <option value="1997">1997</option> <option value="1998">1998</option> <option value="1999" selected="selected">1999</option> <option value="2000">2000</option> <option value="2001">2001</option> <option value="2002">2002</option> <option value="2003">2003</option> <option value="2004">2004</option> </select>
各ヘルパーでは、数値の代わりに日付オブジェクトや時刻オブジェクトをデフォルト値に指定でき、そこから適切な一時コンポーネントを抽出して使います。
任意のオブジェクトのコレクションからセレクトボックス項目のセットを生成したいことがあります。たとえば、以下のようなCity
モデルとそれに対応するbelongs_to :city
関連付けがあるとします。
class City < ApplicationRecord end class Person < ApplicationRecord belongs_to :city end
City.order(:name).map { |city| [city.name, city.id] } # => [["Berlin", 3], ["Chicago", 1], ["Madrid", 2]]
続いて、以下のようなフォームで、ユーザーがデータベースから都市名を選択できるようにします。
<%= form_with model: @person do |form| %> <%= form.select :city_id, City.order(:name).map { |city| [city.name, city.id] } %> <% end %>
belongs_to
関連付けのフィールドをレンダリングするときは、関連付け自体の名前ではなく、外部キー名(上の例ではcity_id
)を指定しなければなりません。
しかしRailsは、コレクションを明示的に列挙せずにセレクトボックス/ラジオボタン/チェックボックスを生成できるヘルパーを提供しています。これらのヘルパーは、コレクション内のオブジェクトごとに指定のメソッドを呼び出して、選択肢の値とテキストラベルを決定します。
collection_select
ヘルパーcollection_select
ヘルパーを使えば、以下のように都市名を選択するセレクトボックスを生成できます。
<%= form.collection_select :city_id, City.order(:name), :id, :name %>
セレクトボックスのHTML出力は以下のようになります。
<select name="person[city_id]" id="person_city_id"> <option value="3">Berlin</option> <option value="1">Chicago</option> <option value="2">Madrid</option> </select>
collection_select
では、第1引数に値のメソッド(上の例では:id
)、第2引数にテキストラベルのメソッド(上の例では:name
)を指定します。この順序は、select
ヘルパーで選択肢を指定する場合(テキストラベルが最初で次が値)と逆である点にご注意ください。
collection_radio_buttons
ヘルパーラジオボタンのセットを生成するには、collection_radio_buttons
ヘルパーを使います。
<%= form.collection_radio_buttons :city_id, City.order(:name), :id, :name %>
ラジオボタンのHTML出力は以下のようになります。
<input type="radio" name="person[city_id]" value="3" id="person_city_id_3"> <label for="person_city_id_3">Berlin</label> <input type="radio" name="person[city_id]" value="1" id="person_city_id_1"> <label for="person_city_id_1">Chicago</label> <input type="radio" name="person[city_id]" value="2" id="person_city_id_2"> <label for="person_city_id_2">Madrid</label>
collection_check_boxes
ヘルパーたとえばhas_and_belongs_to_many
関連付けをサポートする形でチェックボックスのセットを生成するには、collection_check_boxes
ヘルパーを使います。
<%= form.collection_check_boxes :interest_ids, Interest.order(:name), :id, :name %>
チェックボックスのHTML出力は以下のようになります。
<input type="checkbox" name="person[interest_id][]" value="3" id="person_interest_id_3"> <label for="person_interest_id_3">Engineering</label> <input type="checkbox" name="person[interest_id][]" value="4" id="person_interest_id_4"> <label for="person_interest_id_4">Math</label> <input type="checkbox" name="person[interest_id][]" value="1" id="person_interest_id_1"> <label for="person_interest_id_1">Science</label> <input type="checkbox" name="person[interest_id][]" value="2" id="person_interest_id_2"> <label for="person_interest_id_2">Technology</label>
ファイルのアップロードはアプリケーションでよく行われるタスクの1つです(プロフィール写真のアップロードや、処理したいCSVファイルのアップロードなど)。file_field
ヘルパーを使えば、以下のようにファイルアップロード用フィールドをレンダリングできます。
<%= form_with model: @person do |form| %> <%= form.file_field :picture %> <% end %>
ファイルアップロードで最も重要なのは、レンダリングされるフォームのenctype
属性を必ず"multipart/form-data"に設定しなければならない点です。これは、以下のようにform_with
の内側でfile_field_tag
ヘルパーを使えば自動で行われます。enctype
属性は手動でも設定できます。
<%= form_with url: "/uploads", multipart: true do |form| %> <%= file_field_tag :picture %> <% end %>
なお、form_with
の規約に基づいて上述の2つのフィールド名もそれぞれ異なります。つまり前者のフォームではフィールド名がperson[picture]
になり(params[:person][:picture]
でアクセス可能)、後者のフォームでは単なるpicture
になります(params[:picture]
でアクセス可能)。
params
ハッシュに含まれるこのオブジェクトは、ActionDispatch::Http::UploadedFile
のインスタンスです。以下のコードスニペットは、アップロードされたファイルを#{Rails.root}/public/uploads
のパスに元のファイル名で保存します。
def upload uploaded_file = params[:picture] File.open(Rails.root.join('public', 'uploads', uploaded_file.original_filename), 'wb') do |file| file.write(uploaded_file.read) end end
ファイルのアップロードが完了すると、ファイルの保存先の決定(DiskやAmazon S3など)、モデルとの関連付け、画像ファイルのリサイズ、サムネイルの生成など、さまざまなタスクが必要になる可能性があります。Active Storageは、こうしたタスクを支援するように設計されています。
form_with
やfields_for
によって生成されるオブジェクトは、ActionView::Helpers::FormBuilder
のインスタンスです。フォームビルダーは、1個のオブジェクトのフォーム要素を表示するのに必要なものをカプセル化します。フォーム用のヘルパーを通常の方法で自作するときに、ActionView::Helpers::FormBuilder
のサブクラスを作成してそこにヘルパーを追加することも可能です。
たとえば、アプリケーション内でtext_field_with_label
というヘルパーメソッドが以下のように定義されているとします。
module ApplicationHelper def text_field_with_label(form, attribute) form.label(attribute) + form.text_field(attribute) end end
<%= form_with model: @person do |form| %> <%= text_field_with_label form, :first_name %> <% end %>
上のコードは以下のように置き換えることもできます。
<%= form_with model: @person, builder: LabellingFormBuilder do |form| %> <%= form.text_field :first_name %> <% end %>
上の結果を得るには、以下のようなLabellingFormBuilder
クラスを定義しておきます。
class LabellingFormBuilder < ActionView::Helpers::FormBuilder def text_field(attribute, options={}) label(attribute) + super end end
このクラスを頻繁に再利用する場合は、以下のようにlabeled_form_with
ヘルパーを定義してbuilder: LabellingFormBuilder
オプションを自動的に適用してもよいでしょう。
module ApplicationHelper def labeled_form_with(model: nil, scope: nil, url: nil, format: nil, **options, &block) options.merge! builder: LabellingFormBuilder form_with model: model, scope: scope, url: url, format: format, **options, &block end end
ここで使われているフォームビルダーは、以下のコードが実行された時の動作も決定します。
<%= render partial: f %>
f
がActionView::Helpers::FormBuilder
のインスタンスである場合、このコードはform
パーシャルを生成し、そのパーシャルオブジェクトをフォームビルダーに設定します。このフォームビルダーのクラスがLabellingFormBuilder
の場合 、代わりにlabelling_form
パーシャルがレンダリングされます。
フォームから受け取る値は、params
ハッシュのトップレベルに置かれるか、他のハッシュの中に入れ子になって含まれます。たとえば、Person
モデルの標準的なcreate
アクションでは、params[:person]
はその人物について作成されるすべての属性のハッシュになります。params
ハッシュには配列やハッシュの配列なども含められます。
HTMLフォームは原理的に、いかなる構造化データについても関知しません。フォームが生成するのはすべて名前と値のペア(どちらも単なる文字列)です。これらのデータをアプリケーション側で参照したときに配列やハッシュになっているのは、Railsで使われているパラメータ命名ルールのおかげです。
配列とハッシュは、基本的な2大データ構造です。ハッシュは、params
の値にアクセスする時に使われる文法に反映されています。たとえば、フォームに以下が含まれているとします。
<input id="person_name" name="person[name]" type="text" value="Henry"/>
このとき、params
ハッシュの内容は以下のようになります。
{'person' => {'name' => 'Henry'}}
コントローラ内でparams[:person][:name]
でアクセスすると、送信された値を取り出せます。
ハッシュは、以下のように必要に応じて何階層でもネストできます。
<input id="person_address_city" name="person[address][city]" type="text" value="New York"/>
上のコードによってできるparams
ハッシュは以下のようになります。
{'person' => {'address' => {'city' => 'New York'}}}
通常のRailsは、重複したパラメータ名を無視します。パラメータ名に空の角かっこ[]
が含まれている場合、パラメータは配列の中にまとめられます。たとえば、複数の電話番号を入力できるようにしたい場合、フォームに以下を置くことができます。
<input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/> <input name="person[phone_number][]" type="text"/>
これにより、params[:person][:phone_number]
は入力された電話番号の配列になります。
これらの2つの概念を混ぜて使うことも可能です。たとえば、前述の例のようにハッシュの1つの要素を配列にすることも、複数のハッシュを配列にすることもできます。以下のようにフォームの一部を繰り返すことで、任意の数の住所を作成できるようなフォームも作成可能です。
<input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/> <input name="person[addresses][][line1]" type="text"/> <input name="person[addresses][][line2]" type="text"/> <input name="person[addresses][][city]" type="text"/>
上のフォームではparams[:person][:addresses]
ハッシュが作成されます。これはline1
、line2
、city
をキーに持つハッシュの配列です。
ただしここで1つ制限があります。ハッシュはいくらでもネストできますが、配列は1階層しか使えません。配列はたいていの場合ハッシュで置き換えられます。たとえば、モデルオブジェクトの配列の代わりに、モデルオブジェクトのハッシュを使えます。このキーではid、配列インデックスなどのパラメータが利用できます。
配列パラメータは、check_box
ヘルパーとの相性がよくありません。HTMLの仕様では、オンになっていないチェックボックスからは値が送信されません。しかし、チェックボックスから常に値が送信される方が何かと便利です。そこでcheck_box
ヘルパーでは、同じ名前で予備の隠し入力を作成しておき、本来送信されないはずのチェックボックス値が見かけ上送信されるようになっています。チェックボックスがオフになっていると隠し入力値だけが送信され、チェックボックスがオンになっていると本来のチェックボックス値と隠し入力値が両方送信されますが、このとき優先されるのは本来のチェックボックス値の方です。
fields_for
ヘルパーの:index
オプションたとえば、各個人の住所に対応するフィールドのセットを持つフォームをレンダリングしたいとします。こんなときはfields_for
ヘルパーと:index
オプションが役に立ちます。
<%= form_with model: @person do |person_form| %> <%= person_form.text_field :name %> <% @person.addresses.each do |address| %> <%= person_form.fields_for address, index: address.id do |address_form| %> <%= address_form.text_field :city %> <% end %> <% end %> <% end %>
この個人が2つの住所を持っていて、idがそれぞれ23と45だとすると、上のフォームから以下のようなHTMLが出力されます。
<form accept-charset="UTF-8" action="/people/1" method="post"> <input name="_method" type="hidden" value="patch" /> <input id="person_name" name="person[name]" type="text" /> <input id="person_address_23_city" name="person[address][23][city]" type="text" /> <input id="person_address_45_city" name="person[address][45][city]" type="text" /> </form>
このときのparams
ハッシュは以下のようになります。
{ "person" => { "name" => "Bob", "address" => { "23" => { "city" => "Paris" }, "45" => { "city" => "London" } } } }
フォームビルダーのperson_form
でfields_for
を呼び出したので、フォームのすべてのinputは"person"
ハッシュに対応付けられます。:index
オプションを指定したことで、addressの入力は person[address][city]
ではなく、person[address][#{address.id}][city]
に対応付けられました。このように、params
ハッシュを処理する際にAddressのどのレコードを変更すべきかを決定できるようになります。
:index
オプションには他の数値や文字列も渡せます。また、nil
も渡せます(この場合は配列パラメータが作成されます)。
さらに複雑な入れ子を作る場合は、入力名の冒頭部分(上の例ではperson[address]
)を以下のように明示的に指定できます。
<%= fields_for 'person[address][primary]', address, index: address.id do |address_form| %> <%= address_form.text_field :city %> <% end %>
上のコードから以下のHTMLが生成されます。
<input id="person_address_primary_23_city" name="person[address][primary][23][city]" type="text" value="Paris" />
text_field
などのヘルパーに直接:index
オプションを渡すことも可能ですが、通常は個別のinputフィールドで指定するよりも、フォームビルダレベルで指定する方がコードの繰り返しを減らせます。
一般に、最終的な入力名は「fields_for
やform_with
に渡された名前」「:index
オプションの値」「属性名」を連結したものになります。
最後に、index: address.id
のように:index
でIDを指定する代わりに、以下のように指定の名前に"[]"
を追加するショートカットも利用できます。
<%= fields_for 'person[address][primary][]', address do |address_form| %> <%= address_form.text_field :city %> <% end %>
つまり、上のコードで元のサンプルコードと完全に同じHTML出力を得られます。
外部リソースに何らかのデータを渡す必要がある場合も、Railsのフォームヘルパーを用いてフォームを作成する方がやはり便利です。しかし、その外部リソースに対してauthenticity_token
を設定しなければならない場合にはどうしたらよいでしょう。これは、form_with
オプションにauthenticity_token: '外部トークン'
パラメータを渡すことで実現できます。
<%= form_with url: 'http://farfar.away/form', authenticity_token: 'external_token' do %> Form contents <% end %>
支払用ゲートウェイなどの外部リソースに対してデータを送信する場合、authenticity_token
隠しフィールドを生成すると、フォームで使われる可能性のあるフィールドが外部APIによって制限されて不都合が生じることがあります。トークンの送信を抑制するには、:authenticity_token
オプションにfalse
を渡します。
<%= form_with url: 'http://farfar.away/form', authenticity_token: false do %> Form contents <% end %>
最初は単一のオブジェクトを編集していただけのシンプルなフォームも、やがて成長し複雑になります。たとえば、Person
を1人作成するのであれば、そのうち同じフォームで複数の住所レコード(自宅と職場など)を登録したくなるでしょう。後でPerson
を編集するときに、必要に応じて住所の追加・削除・変更も行えるようにする必要があります。
Active Recordはaccepts_nested_attributes_for
メソッドでモデルレベルのサポートを提供しています。
class Person < ApplicationRecord has_many :addresses, inverse_of: :person accepts_nested_attributes_for :addresses end class Address < ApplicationRecord belongs_to :person end
上のコードによってaddresses_attributes=
メソッドがPerson
モデル上に作成され、これを用いて住所の作成、更新、および削除(必要な場合)を行なえます。
ユーザーは以下のフォームを用いてPerson
とそれに関連する複数の住所を作成することができます。
<%= form_with model: @person do |form| %> Addresses: <ul> <%= form.fields_for :addresses do |addresses_form| %> <li> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> <%= addresses_form.label :street %> <%= addresses_form.text_field :street %> ... </li> <% end %> </ul> <% end %>
ネストした属性が関連付けに渡されると、fields_for
ヘルパーはその関連付けのすべての要素を一度ずつ出力します。特に、Person
に住所が登録されていない場合は何も出力しません。フィールドのセットが少なくとも1つはユーザーに表示されるように、コントローラで1つ以上の空白の子要素を作成しておくというのはよく行われるパターンです。以下の例では、Personフォームを新たに作成したときに2組の住所フィールドがレンダリングされます。
def new @person = Person.new 2.times { @person.addresses.build } end
fields_for
ヘルパーはフォームフィールドを1つ生成します。accepts_nested_attributes_for
ヘルパーが受け取るのはこのようなパラメータの名前です。たとえば、2つの住所を持つユーザーを1人作成する場合、送信されるパラメータは以下のようになります。
{ 'person' => { 'name' => 'John Doe', 'addresses_attributes' => { '0' => { 'kind' => 'Home', 'street' => '221b Baker Street' }, '1' => { 'kind' => 'Office', 'street' => '31 Spooner Street' } } } }
:addresses_attributes
ハッシュのキーはここでは重要ではありません。各アドレスのキーが重複しないことが必要です。
関連付けられたオブジェクトが既に保存されている場合、fields_for
メソッドは、保存されたレコードのid
を持つ隠し入力を自動生成します。fields_for
にinclude_id: false
を渡すことでこの自動生成をオフにできます。
コントローラ内でパラメータをモデルに渡す前に、定番のパラメータの許可リストチェックを宣言する必要があります。
def create @person = Person.new(person_params) # ... end private def person_params params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street]) end
accepts_nested_attributes_for
にallow_destroy: true
を渡すと、関連付けられたオブジェクトをユーザーが削除することを許可できます。
class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, allow_destroy: true end
あるオブジェクトの属性のハッシュに、キーが_destroy
で、値がtrue
と評価可能(1、1
、true、true
など)な組み合わせがあると、そのオブジェクトは削除されます。以下のフォームではユーザーが住所を削除可能です。
<%= form_with model: @person do |form| %> Addresses: <ul> <%= form.fields_for :addresses do |addresses_form| %> <li> <%= addresses_form.check_box :_destroy %> <%= addresses_form.label :kind %> <%= addresses_form.text_field :kind %> ... </li> <% end %> </ul> <% end %>
このとき、コントローラ内でパラメータの許可リストを以下のように更新して、_destroy
フィールドが必ずパラメータに含まれるようにしておく必要があります。
def person_params params.require(:person). permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy]) end
ユーザーが何も入力しなかったフィールドを無視できれば何かと便利です。これは、:reject_if
procをaccepts_nested_attributes_for
に渡すことで制御できます。このprocは、フォームから送信された属性にあるハッシュごとに呼び出されます。このprocがtrue
を返す場合、Active Recordはそのハッシュに関連付けられたオブジェクトを作成しません。以下の例では、kind
属性が設定されている場合にのみ住所オブジェクトを生成します。
class Person < ApplicationRecord has_many :addresses accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?} end
代わりにシンボル:all_blank
を渡すこともできます。このシンボルが渡されると、_destroy
の値を除くすべての属性が空白レコードを受け付けなくなるprocが1つ生成されます。
多くのフィールドセットを事前にレンダリングする代わりに、「新しい住所を追加」ボタンを押したときだけこれらのフィールドを動的に追加したいことがあります。Railsにはこのためのサポートは組み込まれていません。フィールドセットを動的に生成する場合は、関連する配列のキーが重複しないよう注意しなければなりません。JavaScriptで現在の日時(エポック時間からのミリ秒の経過時間)を取得して一意の値を得るのが定番の手法です。
フォームのフィールドをフォームビルダーのコンテキストの外でレンダリングする必要が生じたときのために、よく使われるフォーム要素を生成するタグヘルパーを提供しています。たとえば、check_box_tag
は以下のように使えます。
<%= check_box_tag "accept" %>
上のコードから以下のHTMLが生成されます。
<input type="checkbox" name="accept" id="accept" value="1" />
一般に、これらのヘルパー名は、フォームビルダーのヘルパー名の末尾に_tag
を追加したものになります。完全なリストについては、FormTagHelper
APIドキュメントを参照してください。
form_tag
やform_for
の利用についてRails 5.1でform_with
が導入されるまでは、form_with
の機能はform_tag
とform_for
に分かれていました。form_tag
およびform_for
は、禁止ではないものの、利用は推奨されていません。これらのメソッドの利用方法については、旧バージョンのガイドを参照してください。
Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。
原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨
本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
Railsガイド運営チーム (@RailsGuidesJP)
Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。