scaffold全体の流れから分かったこと。
重複するところもあるかもしれないが、scaffoldが生成するコード全体の流れから理解できたことを書き留めておく。(表面的な動きを見て、経験則で理解したことなので、おそらくRailsの中の本質的な考え方は違っていると思うが...。)
処理はコントローラからビューの順で流れる
- コントローラのメソッドを実行し、特にページ描画の指定がなければ、自動的にメソッド名と同じrhtmlファイルを利用して描画する。(例:listメソッドが呼び出されると、list.rhtmlを利用してページ描画する。)
- renderやredirect_toでページ描画を実行していれば、メソッド名と同じrhtmlファイルでの自動描画はしない。
- 対応するrhtmlファイルが存在しないと、白紙のページが表示される。
- 1回の処理の中で、2回以上renderやredirect_toでページ描画を実行してしまうとエラーが発生する。
@で始まるインスタンス変数について
セッションとフラッシュ
- セッションまたはフラッシュを利用すれば、前の処理の情報を、次の処理へ渡すことが出来る。(インスタンス変数では次の処理へ情報を渡すことが出来ない。)
- セッションによる保存は、サーバに接続中ずっと保持される。(サーバに接続中の定義が分からない...。)
- フラッシュによる保存は、次の処理まで保持される。(例:updateメソッドの中で、flash[:notice]に「更新が成功した」というメッセージを保存して、showメソッドに処理を転送(redirect_to)して、show.rhtmlとsoftwares.rhtmlによってページ描画)
- 「更新が成功した」「エラーが発生した」などのメッセージを、次の処理で描画する時に便利な仕組みである。*3
以下はフラッシュのコードの流れ。
app/controllers/softwares_controller.rb:ここでフラッシュにメッセージを入れて、次の処理へ転送
def update @software = Software.find(params[:id]) if @software.update_attributes(params[:item]) flash[:notice] = 'Software was successfully updated.'......更新が成功したら、flash[:notice] にメッセージを保存する。 redirect_to :action => 'show', :id => @software......次の処理showメソッドに処理を転送する。 else render :action => 'edit' end end
editからupdateへの情報の流れを再確認
理解を深めるために、@softwareを@itemに置き換えて見直してみた。
http://0.0.0.0:3000/softwares/edit/6
上記URL入力を受け取ると...
softwares_controller.rb
def edit @item = Software.find(params[:id])......[1] end
変数@itemに、データベースのsoftwaresテーブルのidが6の行への参照情報*4を代入する。
edit.rhtml
<h1>Editing software</h1> <%= start_form_tag :action => 'update', :id => @item %>......[A]、[2] <%= render :partial => 'form' %> <%= submit_tag 'Edit' %>......[B] <%= end_form_tag %> <%= link_to 'Show', :action => 'show', :id => @item %> | <%= link_to 'Back', :action => 'list' %>
[A]で、変数@itemの示すデータベースの情報を、_form.rhtmlの対応する項目に表示して、入力フォームを開始する。
[B]で作成されたEditボタンが押されると、updateメソッドを変数@itemの示すidで実行する。
_form.rhtml
<%= error_messages_for 'item' %>......[3] <p><label for="item_title">Title</label><br/>......[4] <%= text_field 'item', 'title' %></p>......[5] <p><label for="item_description">Description</label><br/> <%= text_area 'item', 'description' %></p> <p><label for="item_url">Url</label><br/> <%= text_field 'item', 'url' %></p>
[3]で、@itemの示すモデルSoftwareの看板errorsの内容を表示している。
[5]で、@itemのtitle項目の内容を表示して、入力フォームのテキストフィールドを作成する。
-
- テキストフィールドを区別する名前として、item[title]を使用する。
- ラベルのfor項目にitem_titleを設定することで、このテキストフィールドと関連付け出来る。
softwares_controller.rb
def update @item = Software.find(params[:id])......[C]、[6] if @item.update_attributes(params[:item])......[D]、[7] flash[:notice] = 'Software was successfully updated.' redirect_to :action => 'show', :id => @software else render :action => 'edit' end end
updateメソッドは二つの情報を受け取って実行される。
-
- 一つ目は、webブラウザのアドレス欄に表示されている情報。つまり、GETによる送信。http://0.0.0.0:3000/softwares/show/6
- 二つ目は、フォームデータとして入力された情報。つまり、POSTによる送信。この情報は目に見えていない。
paramsを使うと、GET、POSTどちらで送信された情報であっても、同じ構文でキーワードを指定するだけで簡単に取り出すことが出来る。
-
- params[:id]では、GETで送信された情報のid部分「6」を取り出している。
- params[:item]では、POSTで送信された情報の、名前がitemの項目を取り出している。(入力フォームにはitem[title]、item[description]、item[url]と名前が付いているので、3つの情報が取り出されるようだ。)
[C]で、データベースのsoftwaresテーブルのid=6の行への参照情報*5が変数@itemに代入される。
[D]で、@itemの示すデータベース上の対象行を、入力フォームからparams[:item]で取り出した3つの情報で更新する。
つまり、以下7箇所のitemは関連しているようだ。
-
- [1] @item = Software.find(params[:id])
- [2] <%= start_form_tag :action => 'update', :id => @item %>
- [3] <%= error_messages_for 'item' %>
- [4] <label for="item_title">
- [5] <%= text_field 'item', 'title' %>
- [6] @item = Software.find(params[:id])
- [7] if @item.update_attributes(params[:item])
- validateを利用してみて、太字の@itemについても関連していることが判明したので修正した。
- もし@itemを利用してデータベースとフォームの入力値を同期するなら、フォームのキーワードには'item'を利用する必要があるのだ。