10. フォーム¶
フォームによるデータ送信の仕組みについて知らない人は、まず次の資料を読むこと。
10.1. Railsにおけるフォーム機能¶
フォームを作るためには、次の二つの作業が必要である。
フォーム画面を表示するビューを作る。
フォームからのデータを受け取って処理をするコントローラのメソッドを作る。
10.1.1. ビューを作る¶
フォームの例として、 scaffold
が作った、新しい学生のデータを入力するページ http://localhost:3000/students/new を見てみよう。このページのビュー app/views/students/new.html.erb
をエディタで開いてブラウザ画面と比べてみよ。
フォームの部分は
render 'form'
と書いてある。render
は他のERBファイルを呼び出す。呼び出されるERBファイルはパーシャルと呼ぶ。この場合は
app/views/students/_form.html.erb
(パーシャルのファイル名は_
で始まる)の処理結果がここに入る。student: @student
はパーシャルへ渡すデータの指定。@student
の値がstudent
という変数に格納される。
それでは app/views/students/_form.html.erb
の内容を見てみる。
1行目、
form_with
はフォーム全体を作るためのメソッド。変数
student
には@student
に入っていたStudent
クラスのインスタンスが入っている。変数
form
には、フォームを生成するためのクラス(FormBuilder)のインスタンスが入る。
2行目から12行目はエラーメッセージを出すための仕組み。とりあえず今は考えない。
15行目、
form.label
はHTMLのlabel
要素を生成する。第1引数にモデルのカラム名を書く。
第2引数以降には画面に表示したい文字列とオプションを書く。文字列を省略するとカラム名が表示される。
25行目、
form.submit
はSubmitボタンを生成する。第1引数に画面に表示したい文字列を書く。省略可。
10.1.2. フォームのデータを受け取る¶
HTMLのソースを見ると、出来上がったフォームの最初のところに action="/students" method="post"
という指定がある。このフォームで送られるデータはどこが受け取るかというと、
% rails routes -c students
でルーティングの表を出して、当てはまるURLパターンとVerb(メソッドと言うとRubyのメソッドと紛らわしいのでVerbと言う)を探せばよい。そうすると、 students
コントローラの create
メソッドであることが分かる。
エディタで app/controllers/students_controller.rb
を見てみる。
create
メソッドは23行目にある。24行目で新しい
Student
のインスタンスを作っている。student_params
はフォームに入力されたデータからカラム名に対応するものだけを取り出すメソッド。67行目で定義されている。最初に
scaffold
で作った時は学年を指定していなかったので、学年を入力したい場合はstudent_params
を修正する必要がある( 入力可能な項目名の指定 のところで説明する)。
26行目の
respond_to
は、出力をHTMLとJSONで切り替えるためのもの。普通にブラウザからリクエストを出した時は
format.html { }
の部分が実行される。とりあえず
format.json { }
の部分は気にしなくてよい。自分でメソッドを書く時は、JSONが必要なければそもそもrespond.to
とかformat.html
とか書かなくてよい。
28行目、
redirect_to
はブラウザに別のページへのリダイレクトを指示する。第1引数はURL。普通はヘルパーメソッドを書く。
student_url
は、rails routes
で表示されるPrefixがstudent
のURIパターン/students/:id
を返す。パラメータ:id
の部分は、引数の学生インスタンスの番号が入る。第2引数はハッシュの形でオプションを書く。ここでは
notice:
でリダイレクトされた先のページで表示したいメッセージを指定している。 * リダイレクトすると、ブラウザからの新しいリクエストの処理になるので、メッセージをインスタンス変数に記憶しておいてもダメ。リクエストをまたがって記憶しておく仕組み(flash)を使う。 * 当然、リダイレクトされた先のページでnotice
のメッセージを表示するようにビューを書いておく必要がある(app/views/students/show.html.erb
の1行目)。
10.2. Railsの処理の流れ大追跡¶
新しい学生のデータを入力するとどうなるか追いかけてみよう。
ブラウザで
http://localhost:3000/students/new
にアクセスする。ルーティング(
scaffold
によって作られたリソースルーティング)に従ってstudents
コンロトーラのnew
メソッドが呼び出される。app/controllers/students_controller.rb
のdef new
のところを見よ。@student = Student.new
により、学生の新しいインスタンスができて@student
に記憶される。レンダリングの指定がないので、デフォルトの
new.html.erb
が呼び出される。app/views/students/new.html.erb
を見よ。render 'form'
により_form.html.erb
が呼び出される。app/views/students/_forms.html.erb
を見よ。form_with ... do |form|
からend
までがHTMLのform
要素を生成する。_form.html.erb
の実行結果がnew.html.erb
のrender 'form'
のところに挿入され、new.html.erb
全体の実行結果がブラウザの画面に表示される。ユーザがデータを入力してSubmitボタンを押す。するとブラウザは
http://localhost:3000/students
にPOSTメソッドでアクセスする。ルーティングによって
students
コントローラのcreate
メソッドが呼び出される。app/controllers/students_controller.rb
のdef create
のところを見よ。params
にはフォームに入力したデータがハッシュの形で入っている。student_params
はハッシュからname
とfaculty_id
だけを取り出す。これを使って学生のインスタンスを新しく作る。save
でデータベースへの保存が成功すればformat.html
の部分を実行する。redirect_to
は、ブラウザに別のページへのリダイレクトを指示するレスポンスを返す。ブラウザが
redirect_to
で指示されたURLでリクエストを出す。ルーティングによって
students
コントローラのshow
メソッドが呼び出される。app/controllers/students_controller.rb
の2行目にbefore_action :set_student
と書いてあるので、show
メソッドを実行する前にset_student
メソッドが実行される。def set_student
のところを見よ。params[:id]
にはURLで指定された番号が入っている。find
でその番号のデータを探して@student
に入れる。def show
のところを見よ。何も書いていないので、すぐにデフォルトのビューが呼び出される。app/views/students/show.html.erb
を見よ。show.html.erb
の実行結果がブラウザの画面に表示される。この時、notice
メソッドによってredirect_to
で指定したメッセージが表示される。
10.3. 選択肢を出す¶
scaffold
が作った _form.html.erb
では、学部を指定するのに form.number_field
を使って番号を入力するようになっているが、学部の番号なんか覚えているわけがないので、学部名の選択肢から選ぶようにしたい。メニュー形式の入力要素は FormBuilder
(変数 form
に入っているやつ)の select
メソッドで作れる。
<%= form.select :faculty_id, {'総合政策' => 1, '環境情報' => 2} %>
しかし、学部名と学部番号の情報はデータベースに入っているので、それと同じことをここでもう一回書くのは避けるべきである。モデルを使って選択肢を出すには次のようにする。
<%= form.collection_select :faculty_id, Faculty.all, :id, :name %>
これは、 Faculty.all
で取り出したインスタンスのそれぞれに対して、 id
と name
の組がメニューの項目になる。メニューの値が先で、表示文字列が後(ハッシュで書く時とは逆)なことに注意。
10.4. 入力可能な項目名の指定¶
最初に scaffold
で作った時には、名前と学部しかなかったので、このフォームには学年を入力するところがない。学年を入力できるようにしてみよう。
まず、 app/views/students/_form.html.erb
に入力欄を追加する。
<div>
<%= form.label :grade, style: "display: block" %>
<%= form.number_field :grade %>
</div>
次に、 app/controllers/students_controller.rb
で、学年の入力データをデータベースに書き込んでもよいという許可をする。
注釈
昔はブラウザから送られてきたデータを何も考えずにデータベースに書き込んでいたが、そうすると、本来はユーザが書き換えてはいけないカラムも書き換えられてしまう。これを悪用した攻撃が可能なので、今はブラウザからのデータを書き込んでもよいカラム名を指定するようになった。
params.require(モデル名).permit(カラム名)
で指定する。scaffold
で作った場合は、自動的にできている(この場合はstudent_params
メソッドの中)ので、それを修正する。
def student_params
params.require(:student).permit(:name, :faculty_id, :grade)
end
10.5. ラジオボタン¶
学年は 1, 2, 3, 4 のどれかなので、数値入力ではなくラジオボタンにしよう。 grade
の値を1にするラジオボタンは次のように書く。
<%= form.radio_button :grade, 1 %>
これだけだとボタンしか表示されないので、普通は横に form.label
で人間に分かる表示をする。ただし、ラジオボタンは同じ項目名でたくさんボタンがあるので、項目名だけではどのラベルがどのボタンに対応しているかわからない。そのため、 form.label
は次のように value:
をキーにして対応するボタンの値も書く。
<%= form.label :grade, '1年', value: 1 %>
ちなみにRailsのデフォルトのスタイルでは (Rails7.1ではインライン要素になったので、何もしなくてよい。)label
がブロック要素になっているので、縦に並ぶ。横に並べたい時は app/assets/stylesheets/scaffold.scss
を修正。
課題18
app/views/students/_forms.html.erb
を修正し、学年をラジオボタンで入力するようにせよ。だらだら書き並べずに繰り返しで書くこと。
【解答例】
10.6. データベースをリセットする¶
動作をテストする時にはデータを入力するが、調子にのっていろいろ入力すると、それが全部データベースに溜まっていくので、うざい。時々ゴミを一掃して何も無い状態からやり直したくなるが、それには、データベースを作り直す次のコマンドを使う。
% rails db:reset
しかし、学部や科目のデータは常に入っていないと困る。そのような固定的なデータは db/seeds.rb
で作るようにしておくと、データベースを作り直す時には自動的に実行される。
Faculty.create(name: '総合政策')
Faculty.create(name: '環境情報')
Course.create(name: '体育1', credit: 1, compulsory: true)
Course.create(name: 'プログラミング言語論', credit: 2, compulsory: false)
なお、 Faculty.create
は Faculty.new
と save
を一度にやるメソッド。
10.7. 今日のまとめ¶
課題19
最終課題のアプリケーションを、そろそろ出来る部分から作り始めよ。まだコードの提出は不要。
レポートは最後にやっつけで書く人が多いが、作りながら少しずつ書きためていく方がよい。構成はだいたい次のような感じで。
最初にどのようなものを作ろうと思ったか、動機や目標や背景。
できたアプリケーションの使い方の説明。
アプリケーションの内部構造の説明。
思った通りのものができたか、何が難しかったか。
動機や目標や背景はすでに書けるはず。今書いたものが全部できる必要はなく、できなかった部分については4でできなかった理由を書けばよい。今週の提出はレポートの1(になるはず)の部分。