今回はRailsにおけるデータの作成と保存、そして変更について説明します。 そのベースになるモデルとデータベースの話から始め、appendとchangeの動作について詳しく説明します。
MVC
MVCはモデル、ビュー、コントローラを指すことばで、Railsの構成を指しています。
- M(Model): モデルは保存対象のデータをオブジェクトにしたもの。 データベースに保存する
- V(View): クライアントに送るHTML文書を作成する
- C(Controller): モデルやビューの働きを調整しコントロールする
ビューについては前回の記事で扱ったので、イメージはつかめると思います。 今回はモデルを定義し、さらに全体を調整するコントローラも作成します。
モデルとデータベース
WordBookでは保存するデータは「英単語と日本語訳」のペアです。 ブラウザ画面から単語をRails側に送信すると、そのモデルが作成されデータベースに登録されます。 モデルはデータベースのレコードに1対1に対応するものです。 モデルはデータベースの1レコードに対応するものなので、単数形でクラス定義をします。 「class Word」というわけです。 コントローラが複数形「class Words」だったのと比べてみてください。
Railsのgenerateコマンドでモデルを作成できます。 引数にデータベースのフィールドを付けておきます。 今回は
- 英単語=>フィールド名en、タイプは文字列(string)
- 日本語訳=>フィールド名jp、タイプは文字列(string)
とします。
$ bundle exec rails generate model Word en:string jp:string invoke active_record create db/migrate/20221020235423_create_words.rb create app/models/word.rb invoke test_unit create test/models/word_test.rb create test/fixtures/words.yml
表示されたパスのうち、最初の2つがモデルを扱う上で重要なファイルです。
1つ目のファイルは後ほどdb:migrate
のところで説明します。
2つ目のファイルのapp/models/word.rb
はモデルの設定をするファイルです。
このファイルにはWordクラスの定義が書かれており、その中身は空になっています。
ですが、このクラスはApplicationRecordクラスのサブクラスとして定義されていますので、スーパークラスの様々なメソッドを使うことができます。
ファイルに以下のようにvalidatesメソッドを追加します。
class Word < ApplicationRecord validates :en, presence: true, format: {with: /[a-zA-Z]+/}, uniqueness: true validates :jp, presence: true end
validatesはデータをチェックするメソッドです。 データベースに書き込む直前にこのチェックが働き、チェックを通らない場合は書き込みをしません。 また、エラーの内容がモデルに書き込まれます。
- enはデータが存在しなければデータベースに保存できない(空欄にはできない)
- enは英文字の文字列でなければならない(formatのwithには正規表現を使える)
- enはユニークである(すでに同名のデータが存在するならば、データベースに保存できない)。 英語には同じスペルで異なる単語が存在するのだが、この単語帳ではそれらを別のレコードして登録できない。 そのため1つのレコードに日本語訳を2つ書くことで問題を解決する。 これは誤って同一の単語を二重に登録するのを防ぐための措置
- jpはデータが存在しなければデータベースに保存できない(空欄にはできない)
db/migrate/20221020235423_create_words.rb
は「データベースにアクセスするための設定」をするファイルです。
なお、RailsはデフォルトでSQlite3をデータベースに使います。
class CreateWords < ActiveRecord::Migration[7.0] def change create_table :words do |t| t.string :en t.string :jp t.timestamps end end end
CreateWordsクラスがActiveRecord::Migration[7.0]のサブクラスとして定義されています。
その中でchangeメソッドが定義されています。
create_table
メソッドはwordsという名前のデータベース・テーブルを作るものです。
そのテーブルには、文字列フィールドの「en」と「jp」、そしてタイムスタンプ(作成日時と更新日時)のフィールドが作られます。
このメソッドのブロックには現れませんが、idフィールドも自動的に作られ、1、2、3・・・という整数がレコードに割り振られます。
このファイルを用いてデータベース・テーブルを作ります。
db:migrate
コマンドをrailsで実行します。
$ bundle exec rails db:migrate == 20221020235423 CreateWords: migrating ====================================== -- create_table(:words) -> 0.0019s == 20221020235423 CreateWords: migrated (0.0020s) =============================
コマンド名が「migrate」となっていますが、これは英語で「移住」という意味を表します。 データベースでマイグレート(動詞)、あるいはマイグレーション(名詞)というのは、データを「異なるフィールド構成のデータベース」に移すことをいいます。 同じデータベースで、データを保ったまま「フィールド構成を変更する」ときもマイグレーションといいます。 今回はデータベースをはじめて作成したので、既存のデータはなく、上記の意味でのマイグレーションではありません。
「rails db:migrate」の名前から想像できると思いますが、このコマンドはデータベースのマイグレーションにも使えます。 その場合は「create_table」だけでなく「change_table」などのメソッドも使います。
コンソール
ここで、モデルに備わっているいくつかのメソッドを紹介します。 先程定義を書いたWordクラスを例にとります。
- @word = Word.new => Wordクラスのオブジェクト(モデル)を生成し、インスタンス変数@wordに代入する
- @word.en= => @wordの指すオブジェクトのインスタンス変数@enに値を代入する
- @word.en => @wordのインスタンス変数@enの指すオブジェクトの値を取得する
- @word = Word.find_by(en: "good") => データベースから、enフィールドの値が"good"であるデータを取り出し、@wordに代入する
- @word.save => @wordの指すオブジェクトをデータベースに保存する
- @word.delete => @wordの指すオブジェクトをデータベースから削除する
これらのメソッドを試すのにはRailsのコンソールを使うのが良いです。 少々回り道になりますが、コンソールを使ってみましょう。 上記のメソッドを試すと、その式の値が表示されるので何をしているのかがよく分かります。 次の例を丁寧にたどってみてください。
$ bundle exec rails console Loading development environment (Rails 7.0.4) irb(main):001:0> @word = Word.new => #<Word:0x00007f7759446f88 id: nil, en: nil, jp: nil, created_at: nil, updated_at: nil> irb(main):002:0> @word.en="good" => "good" irb(main):003:0> @word.jp="良い" => "良い" irb(main):004:0> @word => #<Word:0x00007f7759446f88 id: nil, en: "good", jp: "良い", created_at: nil, updated_at: nil> irb(main):005:0> @word.save TRANSACTION (0.7ms) begin transaction Word Create (0.8ms) INSERT INTO "words" ("en", "jp", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["en", "good"], ["jp", "良い"], ["created_at", "2022-10-21 04:38:51.334385"], ["updated_at", "2022-10-21 04:38:51.334385"]] TRANSACTION (14.4ms) commit transaction => true irb(main):006:0> @word2=Word.new => #<Word:0x00007f7758452578 id: nil, en: nil, jp: nil, created_at: nil, updated_at: nil> irb(main):007:0> @word2=Word.find_by en: "good" Word Load (0.4ms) SELECT "words".* FROM "words" WHERE "words"."en" = ? LIMIT ? [["en", "good"], ["LIMIT", 1]] => #<Word:0x00007f7759505e10 id: 1, en: "good", jp: "良い", created_at: Fri, 21 Oct 2022 04:38:51.334385000 UTC +00:00, updated_at: Fri, 21 Oct 2022 04:38:51.334385000 UTC +00:00> irb(main):008:0> @word.en == @word2.en => true irb(main):011:0> @word.jp == @word2.jp => true irb(main):012:0> @word.delete Word Destroy (43.0ms) DELETE FROM "words" WHERE "words"."id" = ? [["id", 1]] => #<Word:0x00007f7759446f88 id: 1, en: "good", jp: "良い", created_at: Fri, 21 Oct 2022 04:38:51.334385000 UTC +00:00, updated_at: Fri, 21 Oct 2022 04:38:51.334385000 UTC +00:00> irb(main):013:0> @word = Word.find_by en: "good" Word Load (0.2ms) SELECT "words".* FROM "words" WHERE "words"."en" = ? LIMIT ? [["en", "good"], ["LIMIT", 1]] => nil irb(main):014:0> exit $
データベースとのやりとり(save、find_by、delete)ではSQLコマンドがデータベースに対して発行されているのがわかります。 RailsがSQLを発行してくれるので、プログラマーはSQLを知らなくてもデータベースを使うことができます。 これはRailsを使うアドバンテージのひとつです。
Railsのコンソールはプロンプトがirb(main)
となっています。
irbはRubyのgemで単独で使うこともできます。
単にコマンドラインから「irb」と打ち込めばOKです。
Rubyのドキュメントを参照してください。
単語の新規作成
ナビゲーションバーのAppendをクリックしてから単語登録を完了するまでの一連の流れを確認します。 以下では(C)がクライアント(ブラウザ)側、(S)がサーバ(Rails)側とします。
- (C)ナビゲーションバーのAppendをクリック=>/words/appendへGETメソッドでアクセス
- (S)Wordsコントローラのappendアクション(Wordsクラスのappendメソッド)へルーティング
- (S)ビューで英単語と日本語訳のデータ入力枠、送信ボタンのあるHTMLを組み立て、送信
- (C)単語と日本語訳を入力して送信ボタンをクリック=>/words/createへPOSTメソッドでアクセス
- (S)Wordsコントローラのcreateアクションへルーティング
- (S)送られた単語と日本語訳をWordモデル(Wordクラスのインスタンス)にセットしデータベースに保存
- (S)保存成功の場合=>成功のメッセージと保存した英単語を表示するために/words/show/:idへリダイレクトする。ただし:idは登録したモデルのid
- (S)保存失敗の場合=>失敗のメッセージと入力枠を再表示(レスポンスのステータスコードを「unprocessable_entity」にする)
(注)「unprocessable_entity」は「クライアントのリクエストが正しかったが、その指示が処理できなかった」ことを表します。 サーバはクライアントのリクエストに対してレスポンスを返すときにステータスコードを常に送りますが、 railsが自動的にコードを作ってくれることが多く、プログラムに明示する必要がありませんでした。 Rails7ではTurboという仕組みが使われていて、Turboではformタグでクライアントがデータを送った後のサーバーの対応が決められています。 詳しいことは「Turbo Handbook」を参照してください。 成功=>リダイレクト、失敗=>「unprocessable_entity」ステータスコードで入力画面を再送信、という流れでほぼ良さそうです。
上記の流れではサーバとクライアントの間を2から3往復します。 一度の操作では完了しないことに注意してください。
なお、ナビゲーションバーのchangeをクリックしたときの流れもこれに似ています。 そこで、今回の記事ではappendとchangeの両方を説明します。
appendとchange共用のパーシャル
append(単語の追加)とchange(単語の変更)は処理が似ています。
両者ともformタグを使ってデータをサーバに送信します。
そこで、フォーム部分を共通に使えるパーシャルにしておきます。
パーシャルはプログラムのサブルーチンのようなもので、他のビューから呼び出し、埋め込むことができます。
パーシャルはファイル名の先頭をアンダースコアにします。
以下に_form.html.erb
のリストを示します。
<%= form_with model: @word, url: @path, method: :post do |form| %> <div> <%= form.label :en, "英単語", class: "form-label" %> <%= form.text_field :en, value: @en, class: "form-control" %> <% @word && @word.errors.full_messages_for(:en).each do |message| %> <div class="text-danger"><%= "DB: 英単語は空欄にできません" if message == "En can't be blank" %></div> <div class="text-danger"><%= "DB: 英文字を入力してください" if message == "En is invalid" %></div> <div class="text-danger"><%= "DB: すでに同名の単語が登録されています" if message == "En has already been taken" %></div> <% end %> </div> <div> <%= form.label :jp, "日本語訳", class: "form-label" %> <%= form.text_field :jp, value: @jp, class: "form-control" %> <% @word && @word.errors.full_messages_for(:jp).each do |message| %> <div class="text-danger"><%= "DB: 日本語訳は空欄にできません" if message == "Jp can't be blank" %></div> <% end %> </div> <div class="my-2"> <%= form.submit @submit, class: "btn btn-primary" %> </div> <% end %>
form_with
がformタグを生成するためのRailsのメソッド。form.label
メソッドで、inputタグ上部にラベル(文字列)を表示する。class: "form-label"
はBootstrapのクラスclass="form-label"
をタグの属性に指定する。form.text_field
で、type="text"のinputタグが作成される。 タグの名前を"en"に初期状態での枠内の文字列を@enにする。 @enはコントローラで定義する。class: "form-control"
はBootstrapのクラス- 次の5行は、@wordがnilでなく、
@word.errors.full_messages_for(:en)
が空でない場合にメッセージを表示する。 この「空でない」場合とは、@wordをデータベースに保存しようとしてエラーになった場合である。 もちろん、最初のフォーム送信ではエラーはないが、送信後データ保存時にエラーが発生し、フォームが再送信される場合にメッセージが表示される。 したがって、ここのエラーメッセージはモデルのデータベース登録時のバリデーション・メッセージである。 - jp(日本語訳)の入力フォームも同様
form.submit
で送信ボタンを表示する。 ボタンに現れる文字列は@submitになるが、これはコントローラで設定する。class: "btn btn-primary"
はBootstrapのボタンデザインのクラス
appendアクション
データ(英語・日本語訳)の新規作成はAppendメニューのクリックによって始まります。
このメニューのリンクは/words/append
に飛びます。
スラッシュから始まるリンクは、完全な形で書くとhttp:.//localhost:3000/words/append
になります。
このリンク先は/config/routes.rbによって、Wordsコントローラのappendアクション(appendメソッド)にルーティングされます。
Rails.application.routes.draw do get 'words/index' get 'words/show/:id', to: "words#show" get 'words/append' post 'words/create' get 'words/change' post 'words/update' get 'words/delete' delete 'words/exec_delete' get 'words/search' get 'words/list' root "words#index" end
ブロックの上から3番め、get 'words/append'
がそのルーティングです。
HTTPのGETメソッドで/words/appendをアクセスすると、このルーティングにマッチします。
行き先が省略されているときは、そのアドレス通りWordsコントローラのappendアクションに移動します。
Wordsコントローラ(Wordsクラスの定義)の中でappendアクションだけを取り出すと次のようになります。
class WordsController < ApplicationController ... ... ... ... ... ... def append @en = @jp = "" set_append end ... ... ... ... ... ... private def set_append @word = Word.new unless @word @path = words_create_path @submit = "作成" end ... ... ... ... ... ... end
formパーシャルに必要なインスタンス変数はここで設定されます。
- @wordにはWordモデルから新規作成されたオブジェクトが代入される
- @pathには
words_create_path
メソッドの返り値が入る。 このメソッドはroutes.rb
からRailsが自動生成するもので、このルーティングでは'/words/create'が代入される。 なお、このようなメソッドを確認するにはbundle exec rails routes
とコマンドラインから入力する。 これによってルーティングの一覧が表示され、その一番左の列にメソッドが書かれている。 メソッドのないルーティングのルールもある。 - @enと@jpには空文字列が代入される
- @submitには"作成"が代入される
コントローラの処理の中で特に出力コマンドがない場合は対応するビューが呼ばれます。 /app/views/append.html.erbの内容は次のようになります。
<h1 class="my-2">単語登録</h1> <%= render "form" %>
- タイトルとして「単語登録」を表示。 `class="my-2"はBootstrapのクラスでm(マージン)y(上下)を2(デフォルトで2rem)に設定する。 少し上下に隙間ができる
- renderメソッドにより、formパーシャルを呼び出してここに埋め込む。
パーシャルのファイル名にはアンダースコアがつくが(
/app/views/_form.html.erb
)renderの引数ではアンダースコア、拡張子なしで呼び出す
パーシャルのおかげで、非常に短くてすみました。
このような画面が表示されます。 サブミットボタンには@submitの内容である「作成」が表示されています。 このボタンを押すと、formタグで定義されたようにPOSTメソッドで/words/createに送信します。
createアクション
ルーティングにより、createアクションが呼ばれます。
... ... ... post 'words/create' ... ... ...
コントローラのcreateアクションの部分を示します。
class WordsController < ApplicationController ... ... ... ... ... ... def create @en = params[:word][:en] @jp = params[:word][:jp] @word = Word.new(en: @en, jp: @jp) if @word.save flash[:success] = "単語を保存しました" redirect_to "/words/show/#{@word.id}", status: :see_other else set_append flash.now[:alert] = "単語を保存できませんでした" render :append, status: :unprocessable_entity end end ... ... ... ... ... ... end
送信されたデータをparamsメソッドで取り出すことができます。
そのときデータにつけられた名前(例えば「:wordの:en」)はappendビューで作成したフォームタグに書かれたものです。
form_with
でモデルを指定した場合は、モデル名が名前の1番めになります。
form.text_field
の最初の引数が名前の2番めになります。
モデルを指定しない場合は、params[:en]
のようにモデル名なしになります。
- 英単語は
params[:word][:en]
で取り出せる。 - 日本語訳は
params[:word][:jp]
で取り出せる。 - @wordを新規作成する。 そのときenフィールドを@en、jpフィールドを@jpにする。 つまり送信された英単語と日本語訳にする
- saveメソッドにより@wordをデータベースに登録する。
このとき、モデル定義(
/app/models/word.rb
ファイル)に書いたバリデーションが行われる。 すでに書いたように、バリデーションとはデータのチェックのこと。 改めてどのようなバリデーションだったかを書くと- enはデータが存在しなければデータベースに保存できない
- enは英文字の文字列でなければならない
- enはユニークである(すでに同名のデータが存在するならば、データベースに保存できない)
- jpはデータが存在しなければデータベースに保存できない
- バリデーションががOKならば保存が実行され
@word.save
は真になる。 バリデーションを通らなければ保存されず、@wordにエラーメッセージがセットされ、@word.save
は偽になる。 - 無事保存された場合は、フラッシュに「単語を保存しました」を代入し、showアクションにリダイレクトする。
リダイレクトとは、HTTPステータスコードの300番台とリダイレクト先アドレスをクライアントに送り、クライアントからそのアドレスにアクセスしてもらうことをいう。
なお、Turbo Handbookを見るとTurboではフォームのPOST送信後は303(see other)ステータスコードを期待しているとのことである。
それに沿ってプログラムでは、redirectメソッドの最後に
status: :see_other
を付け加えた。 仮にステータスコードの指定を省略すると302(Found)がステータスコードになる。 それも試したところ、正しくリダイレクトされたので、現状は302でも良いのかもしれない。 ともあれ、RailsがTurboを使っている以上リダイレクトしなければならない - 保存に失敗した場合は、ステータスコードunprocessable_entity(データは受け取ったがサーバ側は想定された作業を遂行できなかった)を返し、リダイレクトせずにデータを送信する。 set_appendメソッドにより、@path送信先アドレス、@submitをセットする。 @wordはnilではないのでそのままになる。 このとき保存に失敗したバリデーションのエラー情報が@wordには含まれている。 renderメソッドでappendビューをクライアントに送る。 appendビューはformパーシャルを呼び出す。 このときformパーシャルの中で@wordのエラーメッセージが取り出され、それに対応した日本語メッセージがHTMLに加えられる。 このあとはクライアントからのPOST送信が繰り返されることになる
フラッシュ
フラッシュはリダイレクト後の画面でメッセージを表示するための仕組みです。 フラッシュはHashに似てキーと値を定義できます。
createアクションでデータが無事に保存された場合はflash[:success] = "単語を保存しました"
でフラッシュに登録します。
これは次のshowアクションで取り出されて表示されることになります。
フラッシュはどのアクションでも使う可能性があるので、レイアウトの部分に入れておくのが良いです。
そこでレイアウト/app/views/application.html.erb
を書き直します。
<!DOCTYPE html> <html> ... ... ... ... ... ... <body> <div class="container"> <div class="col-sm-10 col-lg-8 col-xl-6 mx-auto"> <%= render "navbar" %> <%= render "flash" %> <%= yield %> </div> </div> </body> </html>
コンテナで画面サイズの調整がされているのですが、大きな画面ではまだ幅がありすぎるので<div class="col-sm-10 col-lg-8 col-xl-6 mx-auto">
で調整します。
これはすべての画面で適用されます。
その後にナビゲーションバーを表示するパーシャルnavbarを呼び出し、次にフラッシュを表示するパーシャルflashを呼び出します。
navbarはすでにコードを示しましたので、ここではflashのみを示します。
<% flash.each do |name, msg| %> <% if name == "success" %> <%= content_tag :div, msg, class: "text-success" %> <% elsif name == "alert" %> <%= content_tag :div, msg, class: "text-danger" %> <% end %> <% end %>
これを/app/views/_flash.html.erb
というファイル名で保存します。
フラッシュはHashと同じようにeachコマンドを使うことができます。
- successキーの場合は、text-successクラス(緑色)でメッセージを表示
- alertキーの場合はtext-danger(赤色)でメッセージを表示
content_tagメソッドは、ここではdivタグの中にメッセージを入れたHTMLを埋め込みます。
フラッシュはリダイレクトで用いるものですから、次回の送信で反映されます。
- リダイレクト(303ステータスコード+リダイレクト先のアドレス)をクライアントに送信する(送信1回め)
- クライアントはそれに基づいてリダイレクト先にGETメソッドでアクセスする
- サーバがデータを送信する(送信2回め)=>このときフラッシュが有効になる
しかし、リダイレクト無しでフラッシュを使いたいときもあります。 そのときはflash.nowを用います。 flash.nowでは1回めの送信でフラッシュが有効になります。 createアクションの中の次のコードを比べてみてください
if @word.save flash[:success] = "単語を保存しました" redirect_to "/words/show/#{@word.id}", status: :see_other #<=リダイレクトがあるのでflashを使う else set_append flash.now[:alert] = "単語を保存できませんでした" render :append, status: :unprocessable_entity #<=リダイレクト無しなのでflash.nowを使う end
単語の変更
単語の変更はchange、updateアクションで行います。 この2つはappend、createに似ています。
change アクション
appendとの違いは@pathと@submitだけです。
class WordsController < ApplicationController ... ... ... ... ... ... def change @en = @jp = "" set_change end ... ... ... ... ... ... def set_change @word = Word.new unless @word @path = words_update_path @submit = "変更" end end
- @pathによるPOSTメソッドの送信先のアドレスには
wotds_update_path
メソッドを使う。 このメソッドは'/words/update'を返す - @submitは「変更」にする。 これがボタンの文字になる
対応するビューもほとんど同じです。 /app/views/change.html.erbの内容は次のとおりです。
<h1 class="my-2">単語の変更</h1> <%= render "form" %>
appendのビューとの違いはタイトルだけです。 画面のスクリーンショットはほぼ同じなので省略します。 ボタンが押されると、POSTメソッドで先程の送信先にデータが送られます。
なお、Railsでは変更、編集、アップデートではPATCHまたはPUTメソッドが使われます。 そのようにプログラムを変更することはもちろん可能で、@pathで送信先をappendとchangeで区別したのと同様に@method変数に"post"、"patch"を入れて区別すればOKです。 その方がベターかもしれません。
update アクション
POSTメソッドで送信されたデータはroutes.rbによってWordsコントローラのupdateアクションにルーティングされます。
post 'words/update'
コントローラからupdateアクションの部分を取り出してみましょう。
class WordsController < ApplicationController ... ... ... ... ... ... def update @en = params[:word][:en] @jp = params[:word][:jp] @word = Word.find_by(en: @en) if @word == nil set_change flash.now[:alert] = "単語「#{@en}」は未登録のため変更できません" render :change, status: :unprocessable_entity elsif @word.update jp: @jp flash[:success] = "単語を変更しました" redirect_to "/words/show/#{@word.id}", status: :see_other else set_change flash.now[:alert] = "変更した単語を保存できませんでした" render :change, status: :unprocessable_entity end end ... ... ... ... ... ... def set_change @word = Word.new unless @word @path = words_update_path @submit = "変更" end end
- @enと@jpに送られてきた英単語と日本語訳を代入する
- その英単語をもつWordオブジェクトをデータベースから探し出し@wordに代入する。 そのようなデータがなければ@wordにはnilが代入される
- @wordがnilならば
- そうでなければ@wordのjp部分のデータを用いてデータベースを書き換える(update)。
その書き換えが成功した場合は
- フラッシュに「単語を変更しました」をセット
- showアクションへリダイレクト
- データ書き換えに失敗した場合は
やっていることはcreateアクションと似ています。 違いはsaveの代わりにupdateを用いているところです。
- saveは@wordのデータ全部を作成または更新する
- updateは@wordの指定されたフィールドのデータのみを更新する
saveは更新にも使えるのでこちらをupdateの代わりに使うこともできます。
show アクション
createとupdateのリダイレクト先のshowアクションについて説明します。 showアクションは特定の1つの単語について表示します。 どの単語かの指定はアドレスの中に書きます。
words/show/1
=>データベースからid=1の要素を取り出して表示words/show/2
=>データベースからid=2の要素を取り出して表示
このような形になります。 createやupdateでは
redirect_to "/words/show/#{@word.id}", status: :see_other
とい形でshowメソッドにリダイレクトしていました。
#{@word.id}
の部分が取り出したいデータのidになります。
すなわち、createやupdateで作成または変更したデータのidです。
showメソッドへのルーティングは次のようになります。
get 'words/show/:id', to: "words#show"
GETメソッドでwords/show/:id
のアドレスで呼ばれた場合、Wordsコントローラのshowアクションを呼び出します
そのとき、:id
の部分はパラメータになり、params[:id]
でその値を読み出すことができます。
つまり/words/show/10
でアクセスするとparams[:id]
は10になります。
コントローラにおけるアクションは次のようになります。
def show begin @word = Word.find(params[:id]) rescue ActiveRecord::RecordNotFound @word = nil flash.now[:alert] = "データベースにはid=#{params[:id]}のデータは登録されていません" end end
findメソッドは引数がidの値になるようなデータを探して取り出します。
データベースからparams[:id]
の値がidフィールドに書かれているレコードを取り出し、@wordに代入します。
データが取り出せないときには例外が発生しますので、それを捕捉し、@wordをnilにしてフラッシュを作成します。
ここで、beginとrescueの使い方を説明します。 Rubyは例外が発生するとそこでプログラムを停止し、例外の内容を表示します。 プログラムを停止したくない場合はbeginとrescueを使い「例外の捕捉(つかまえること)」をします。 beginとrescueの間に例外が発生する可能性のあるステートメントを書きます。 rescueの次に想定される例外の名前を書きます。 その例外が発生するとrescue節に実行が移るので、そこで例外に対する処置を行います。
ビューは次のようになります。
<% if @word %> <ul class="list-group"> <li class="list-group-item"><%= @word.en %></li> <li class="list-group-item"><%= @word.jp %></li> </ul> <% end %>
@wordがnilのときは何も表示されません。 それでもフラッシュだけは表示されます。
今回はWordbookの単語登録と変更について書きました。 これが全体の3分の1くらいになります。 次回は残りの単語検索と削除を説明します。