Railsの表示、データの編集・削除機能をまとめて生成してくれる超便利コマンドscaffold(スキャッフォルド)があります。
このrails g scaffold モデル名 カラム名:型,,,
を実行したときに自動で生成される、_form.html.erbの中身と、その中で使われている、form_with(model: user) do |form|
やany, pluralizeなどの処理が何をしているかについて解説しています。
_form.html.erbの中身を理解して自由自在にカスタマイズできるようになると、Railsがより一層楽しくなります。
scaffoldって何?詳しく知りたい!という方は下記をご参考ください。
【Rails】超便利コマンドscaffoldの使い方を実例でわかりやすく解説
_form.html.erbの意味や呼び出し方
_form.html.erbとは何か?
_form.html.erbファイルとは、scaffoldを実行したときに自動で生成されるファイルで、指定したカラムの内容を変更するためのフォームです。
ファイル名冒頭のアンダースコアとは何か?
ファイル名の冒頭にアンダースコア_
がつくところが他のビューファイルと大きく異なる点です。
ファイル名冒頭のアンダースコアとは、普通のビューファイルではなく、部分的に呼び出すファイルであることを示しています。
部分的に呼び出すので、部分テンプレートや、部分的なという意味をもつパーシャル(partial)と呼ばれます。
_form.html.erbはどこで使われているか?
scaffoldで自動生成される、_form.html.erbは、データを編集するedit.html.erbと、新規にデータを追加するnew.html.erbの2つのファイルの中で呼び出されています。
呼び出し方は次のようになります。
<%= render 'form', プロパティ名: インスタンスクラス %>
表示するビューファイルやアクションを指定するrenderの後に’form’を記述し、第2引数でモデル経由で取得したデータを渡しています。
renderでは呼び出している対象がパーシャルであることを明示するためにpartialオプションをつけることもできます。(データの渡し方が変わります。localオプションを使います)
以下の2つの記述は同じ処理になります。
render 'form'
render partial: 'form'
データを渡す場合は次の2つが同じ処理になります。
render 'form', user: @user
render partial: 'form', locals: {user: @user}
edit.html.erbの実例
<h1>Editing User</h1>
<%= render 'form', user: @user %>
<%= link_to 'Show', @user %> |
<%= link_to 'Back', users_path %>
new.html.erbの実例
<h1>New User</h1>
<%= render 'form', user: @user %>
<%= link_to 'Back', users_path %>
form_with(model: モデル名) do |form|とは何か?
form_withとは何か?
form_withとはRailsのヘルパーの一つで、データ送信のための入力フォームを簡単に作成するものです。
form_withの使い方は大きく2つあります。
- DBにデータを保存しない(指定したURLにデータを渡す)
- DBにデータを保存する
それぞれで書き方が異なります。
DBにデータを保存しない
DBにデータを保存せず、指定したURLにデータを渡す場合は、 url
オプションを使います。
<%= form_with(url: パス) do |form| %>
フォームの内容
<% end %>
オプションを囲むカッコはあってもなくても同じです。
<%= form_with(url: パス) do |form| %>
<%= form_with url: パス do |form| %>
urlオプションで指定するパスは、prefixに_pathヘルパーをつけた、prefix_path
で指定することが一般的です。
prefixは rails routes
コマンドで確認できます。
例えば、以下であれば、/usersというパスに対応するprefixは users であることがわかります。これに_pathヘルパーをつけて、users_path とすれば、/users という相対パスを生成することができます。(絶対パスにしたい場合は _urlヘルパーを使います)
# rails routes
Prefix Verb URI Pattern Controller#Action
users GET /users(.:format) users#index
なお、prefix_path以外にも、文字列で指定しても機能します。(例: url: “https://example.com/users/show”)
この状態で、<%= form.submit %>
で生成した送信ボタンをクリックすると、指定したURLにデータがPOSTメソッドで送られます。
コンパイルの例
実際に、以下のerbのコードをHTMLにコンパイルした結果は以下のようになります。
<%= form_with(url: home_index_path) do |form| %>
<%= form.submit %>
<% end %>
↓ HTMLにコンパイル
<form action="/home/index" accept-charset="UTF-8" method="post">
<input type="hidden" name="authenticity_token" value="ayMz1Tk_33YWCe-8trG6xxxxxxxxx">
<input type="submit" name="commit" value="Save " data-disable-with="Save ">
</form>
指定したURLにPOSTメソッドでデータを送信するフォームになっています。
DBにデータを保存する
DBにデータを保存する場合は、 model
オプションを使います。
<%= form_with(model: モデルのインスタンス) do |form| %>
フォームの内容
<% end %>
modelオプションには、モデルのインスタンスを指定します。
モデルのインスタンスとは、例えばuserモデル(Userクラス)であれば、User.new
, User.first
, User.find(id番号)
といったものです。
Scaffoldを使った場合は、データを編集するedit.html.erbと、新規にデータを追加するnew.html.erbでどのインスタンスを渡すかがコントローラに記述されています。
edit.html.erbは、@user = User.find(params[:id])
new.html.erbは、@user = User.new
コンパイルの例
実際に、以下のerbのコードをHTMLにコンパイルした結果は以下のようになります。
<%= form_with(model: user) do |form| %>
<%= form.submit %>
<% end %>
↓ HTMLにコンパイル
<form action="/users/1" accept-charset="UTF-8" method="post">
<input type="hidden" name="_method" value="patch">
<input type="hidden" name="authenticity_token" value="ayMz1Tk_33YWCe-8trG6xxxxxxxxx">
<input type="submit" name="commit" value="Update User" data-disable-with="Update User">
</form>
urlオプションとは異なり、コンパイルした結果にpatchメソッドが挿入されています。これで、POSTではなくPATCHとして処理されます。
ルーティングで確認すると、/users/:id にPATCHでアクセスした場合は、usersコントローラのupdateアクションが実行されます。
この結果DBのデータを更新する処理となります。
# rails routes
Prefix Verb URI Pattern Controller#Action
edit_user PATCH /users/:id(.:format) users#update
model: userとは?
form_withヘルパーでmodelオプションを使ってモデルのインスタンスを指定すれば、PATCHメソッドでDBのデータを更新できることがわかりました。
ですが、scaffoldで生成されたコードでは、モデルのインスタンスではなく、オブジェクト(例: user)が指定されています。form_with(model: user)
これは、モデルのインスタンスの省略形です。
例えば、URIが users/5/edit にアクセスしたとき以下は同じ指定になります。
form_with(model: user)
form_with(model: @user)
form_with(model: Users.find(5))
form_with(model: Users.fifth)
(参考)
form_with(model: モデルのインスタンス) do |form| とは?
form_withの後ろにはdo |form|
がついています。
form_withヘルパーは渡された情報を含む、オブジェクトを生成します(FormBuiderクラスのインスタンス)。
このオブジェクトにformという名前を付けて、後ろの処理で使えるようにしています。
FormBuiderクラスには、label, text_field, number_field, submitといった便利なメソッドが用意されているので、formオブジェクトでこれらのメソッドを使うことができます。
なお、オブジェクト名の指定は、|form| 以外に |f| が使われることも多いです。
FormBuiderクラスの主なメソッド
scaffoldで生成された、_form.html.erb内で使われている、FormBuiderクラスの主なメソッドには以下のようなものがあります。
メソッド | 内容 | 実例 |
---|---|---|
label | labelタグを生成する | <%= form.label :name %> |
text_field | type=”text”のinputタグを生成する | <%= form.text_field :name %> |
number_field | type=”number”のinputタグを生成する | <%= form.number_field :age %> |
submit | <%= form.submit %> |
@user = User.find(params[:id])
を渡したときのerbのコードとコンパイル後の結果は以下のようになります。
labelメソッド
オプションで :名前
を渡すと、指定した値を表示します。また、for属性に、渡されたデータ名_名前
が入ります。
idやclass属性を指定する場合は、,id:"id名", class:"class名"
を追記します。{ }で囲っても同じです ,{ id:"id名", class:"class名" }
<%= form.label :name %>
<%= form.label :test_item, id:"test-id", class:"test-class" %>
↓ コンパイル
<label for="user_name">Name</label>
<label id="test-id" class="test-class" for="user_test_item">Test item</label>
▼ブラウザの表示
text_field
オプションで :名前
を渡すと、name属性に渡されたデータ名[名前]
が入ります。id属性に 渡されたデータ名_名前
が入ります。
class属性を指定する場合は、,class:"class名"
を追記します。(カンマで区切ります)
<%= form.text_field :name %>
<br>
<%= form.text_field :name, class:"test-class" %>
↓ コンパイル
<input type="text" value="test1" name="user[name]" id="user_name">
<br>
<input class="test-class" type="text" value="test1" name="user[name]" id="user_name">
value属性には、渡されたデータのnameカラムのデータが入ります。
▼ブラウザの表示
他にも、フォームの幅や文字数の上限を指定するオプションが使えます。
number_field
type=”number”のinputタグを生成します。 type=”number”は数値のみの入力を許可するものです。数値以外の文字は入力することができません。
オプションで :名前
を渡すと、name属性に渡されたデータ名[名前]
が入ります。id属性に 渡されたデータ名_名前
が入ります。
class属性を指定する場合は、,class:"class名"
を追記します。(カンマで区切ります)
<%= form.number_field :age %>
<br>
<%= form.number_field :age, class:"test-class" %>
↓ コンパイル
<input type="number" value="24" name="user日付未入力" id="user_age">
<br>
<input class="test-class" type="number" value="24" name="user日付未入力" id="user_age">
value属性には、渡されたデータのageカラムのデータが入ります。
▼ブラウザの表示
他にも、最大値、最小値などをオプションで指定できます。
submit
type=”submit” name=”commit”のinputタグを生成します。 type=”number”は数値のみの入力を許可するものです。数値以外の文字は入力することができません。
オプションで :名前
を渡すと、name属性に渡されたデータ名[名前]
が入ります。id属性に 渡されたデータ名_名前
が入ります。
class属性の指定方法は、text_fieldやnumber_fieldと異なります。
#valueを指定する場合(カンマ必要)
<%= form.submit 'valueの値', class: "クラス名" %>
#valueを指定しない場合(カンマ不要)
<%= form.submit class: "クラス名" %>
valueを指定しない場合に、カンマをつけて、 ,class="クラス名"
とするとエラーになります。
▼実例
<%= form.submit %>
<br>
<%= form.submit class: "test-class" %>
<br>
<%= form.submit '送信', class: "test-class" %>
↓ コンパイル
<input type="submit" name="commit" value="Update User" data-disable-with="Update User">
<br>
<input type="submit" name="commit" value="Update User" class="test-class" data-disable-with="Update User">
<br>
<input type="submit" name="commit" value="送信" class="test-class" data-disable-with="送信">
▼ブラウザの表示
エラー発生時の表示
_form.html.erbの冒頭のif文処理はDBからデータ取得を失敗したときに表示される内容です。
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
このブロックで使われている、.errors.any?
, pluralize
, error.full_message
について解説します。
.errors.any?とは何か
_form.html.erbの冒頭で、 <% if user.errors.any? %>
のように、渡したデータ(モデルのインスタンス)に対して、.errors.any?
が実行されています。
errorsとはバリデーションの実行結果に対して使うもので、エラーの内容をハッシュで取得するRailsのメソッドです。
.errors.メソッド名
のように、エラー内容に対してメソッドをつけて使います。
any?
はRubyのメソッドで、レシーバー(検証対象のオブジェクト)の中にtrueが1つでもあればtrueを返します。
.errors.any?とすることで、1つでもエラーが発生していれば trueになります。
ここでは、レシーバーのusersは、User.find(params[:id])
または、User.new(user_params)
です。
users
↓
@users
↓
User.find(params[:id]) #editアクション
User.new(user_params) #newアクション
つまり、uesrモデルを使って、usersテーブルからデータを取得しようとしたときにエラーが発生してないかを確認する処理になります。
エラーが発生している場合は次のコードが表示されます。
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
errorsと併せて使う主なメソッド
メソッド | 内容 |
---|---|
errors | エラーの内容をハッシュで取得 |
errors.any? | エラーが発生した場合はtrueになる |
errors.count | エラーの数を返す |
errors.full_messages | エラーの内容を配列に変換 |
errors.full_messages_for(:カラム名) | 指定したカラムのエラーのみを配列で表示 |
user.errors.each do |error|
とすることで、エラーの内容を変数errorに代入して一つ一つ抜き出し、<%= error.full_message %>
とすれば、エラーの内容を画面に表示することができます。
valid?とinvalid?メソッド
errorsメソッド以外にバリデーション結果の確認でよく使うメソッドに、valid?
とinvalid?
があります
validは空という意味です。User.newなどモデルクラスでメソッドを実行したときに、処理に失敗すると、そのオブジェクトは空になります。これを利用してチェックを行うメソッドです。
メソッド | 内容 |
---|---|
valid? | オブジェクトが空でないか確認。データが取得できていればtrueになる。 |
invalid? | valid?の逆。データが取得できていない場合にtrueになる。 |
pluralizeとは?
pluralizeとは、Railsのヘルパーの一つで英語にのみ使います。第1引数で渡された数値が1であれば、その数値と第2引数の値を単数形で返します。2以上であれば、数値と複数形を返します。
pluralize(整数, "英単語")
#例
pluralize(1, "apple") -> 1 apple
pluralize(3, "apple") -> 3 apples
数値無しで単語のみを返したい場合は、単語をレシーバー(対象となるオブジェクト)で指定します。
"英単語".pluralize(整数)
#例
'apple'.pluralize(1) -> apple
'apple'.pluralize(3) -> apples
▼実例
_form.html.erbの中では次のように使われています。
<%= pluralize(user.errors.count, "error") %>
第1引数のuser.errors.countは、 User.find(params[:id])
または、User.new(user_params)
を実行したときに発生したエラーの数を整数で渡します。
エラーの数が1つなら、「1 error」を、2つなら、「2 errors」を返します。