【Rails】ファイル名冒頭のアンダースコアやform_with(model: user) do |form|とは何か?scaffoldで生成される_form.html.erbの中身を完全理解

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。

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つあります。

  1. DBにデータを保存しない(指定したURLにデータを渡す)
  2. 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メソッドでデータを送信するフォームになっています。

type=”hidden”とは?

type=”hidden”とは、inputタグにつけるtype属性の値の一つです。hiddenを指定したinputタグはブラウザ上に表示されなくなります。

ブラウザ上に表示する必要はないけど、送信先にデータを送りたい場合に使います。

authenticity_tokenとは?

authenticity_tokenとは、CSRF対策としてRailsにでデフォルトで実装されている機能です。

POSTやPUTCHなど、GET以外のメソッドでリクエストがあった場合に使います。

inputタグのname=authenticity_tokenとvalueでトークンの情報を渡し、そのトークンが一致したときのみ処理を受け付けます。

トークンは<%= form_authenticity_token %>で生成されています。(form_withヘルパーを使えば自動でauthenticity_token用のinputタグが挿入されます。)

なお、この情報は必須です。ないと以下のようなエラーが発生します。

WARNING: Can’t verify CSRF token authenticity
Completed 500 Internal Server Error in 0ms


CSRFとは?

CSRFとは、セッション管理に関する脆弱性で、第三者が勝手にリクエストを送信して操作を行うことです。(Cross-Site Request Forgeriesの略でクロスサイトフォージェリと呼びます)

あるユーザーがログイン状態の場合に、他のサイトのURLをクリックしたときに、そこにリクエストが仕込んであり、それが実行されてしまうものです。

本来はその別のサイトはログインユーザーではないのですが、自分を経由するためセッション情報(Cookie)が渡ってしまうために発生します。

実質的な被害としては、ブログやSNSに勝手に投稿されるといったものが発生しています。


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

params[:id] とは?

params[:id]とは、URLのパラメータで渡されたパラメータ名idのデータを受け取るための記述です。

idというパラメータはルーティングで決められています。例えば、ユーザー情報を編集するusersコントローラのeditアクションの場合、URIのパターンは /users/:id/edit となっているので、/users/ と /edit で挟まれた値が params[:id] に渡されます。

# rails routes
Prefix    Verb   URI Pattern                  Controller#Action
edit_user GET    /users/:id/edit(.:format)    users#edit

URLがhttps://example.com/users/5/edit であれば、params[:id]は5になります。


コンパイルの例

実際に、以下の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
ポイント

form_withヘルパーで、オプションをurlにするか、modelにするかの大きな違いの一つは、PATCHメソッドにするinputタグを挿入するかどうかの差です。

 <input type="hidden" name="_method" value="patch">

他には、submitなど他のinputタグの値が変わるといった違いがあります。



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クラスの主なメソッドには以下のようなものがあります。

メソッド内容実例
labellabelタグを生成する<%= form.label :name %>
text_fieldtype=”text”のinputタグを生成する<%= form.text_field :name %>
number_fieldtype=”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>

▼ブラウザの表示

labelのfor属性とは?

labelタグはinputタグと併せてて使用するものです。

inputタグと紐づけるためには、inputタグのid属性の値labelタグのfor属性の値を同じにする必要があります。

inputタグとlabelタグを紐づけるユーザビリティ向上のメリットがあります。

  • labelタグをクリックしたときも、inputタグがフォーカスされる。
  • 読み上げ機能があるとき、inputタグにフォーカスするとlabelの内容が読み上げられます。

▼例:labelとinputの紐づけ

<label for="user_nama">Nama</label>
<input type="text" value="test1" name="user[name]" id="user_name">


なお、for属性とid属性で紐づける以外にも、inputタグをlabelタグで囲う方法もあります(どちらも同じです)

<label>Nama
  <input type="text" value="test1" name="user[name]">
</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カラムのデータが入ります。

▼ブラウザの表示

他にも、フォームの幅や文字数の上限を指定するオプションが使えます。

(参考)Railsドキュメント text_field

inputタグのvalue属性とは?

inputタグのvalue属性とは入力欄の初期値になります。

value="" とすればデフォルトは空であることを明示できます。

情報の送信時は、name属性で指定した値と入力欄の内容がセットで送信されるため、未入力のお場合は、name属性の値とvalueの値がセットで送信されます。


なお、初期値をセットするのではなく、薄く記入例を表示したい場合は、placeholder属性を使います。

inputタグのname属性とは?

inputタグのname属性はどのinputタグかを識別するために使います。

例えば、次のようにinputタグが複数ある場合、name属性がないと、どのデータがどのinputタグに入力されたものか区別することができません。

このためname属性は必須です

<input type="text" value="name">
<input type="text" value="nickname">
<input type="text" value="odlname">

inputタグにname属性をつけると、name属性の値=valueの値 というデータも一緒に送られてきます。(データが入力された場合、valueの値は上書きされます)

<input type="text" value="name" name="user-name">
<input type="text" value="nickname" name="user-nickname">
<input type="text" value="oldname" name="user-oldname">

送信されたデータは以下のようになり、どのデータか区別をつけることができます。

user-name=name
user-nickname=nickname
user-oldname=oldname


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カラムのデータが入ります。

▼ブラウザの表示

他にも、最大値、最小値などをオプションで指定できます。

(参考)Railsドキュメント number_field


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」を返します。

タイトルとURLをコピーしました