Rails Ambassador

RailsコンソールでModelを操作する

様々なモデル操作のメソッドを習得していきましょう。

Railsコンソールの起動

モデルと関連するテーブルを作成できましたので、Railsのコンソール(REPL)を使ってモデル操作をしていきましょう。

以下コマンドを実行し、Railsコンソールを立ち上げてください。

$ rails console

以下のように省略形でもOKです。

$ rails c

以下のようにコンソールが起動すればOKです。

irb(main):001:0>

rails consoleの終了

Railsコンソールを終了するにはexitコマンドを使ってください。

irb(main):001:0> exit
xxxx/board $

rails consoleの入力不備

式が途中の場合は改行しても式の終了とはなりません。(例:Post.find(1)の場合)

irb>  Post.find(
irb*   1)
=> #<Post id: 1, content: "最初の投稿です。", created_at: "20xx-mm-dd HH:MM:SS", updated_at: "20xx-mm-dd HH:MM:SS">

クォーテーションなども同様です。

irb> Post.find_by(content: "
irb" hoge")
  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`content` = '\nhoge' LIMIT 1
=> nil

irb”」や「irb*」表記になっていていつもと違うことに注目してください。

new

では早速モデルを操作していきましょう。

始めに学習するメソッドは「new」です。このメソッドはモデルのクラスメソッドとして定義済みのメソッドです。
モデルクラスにこのメソッドを使うとモデルの新規インスタンスを返します。
ではrailsコンソールを立ち上げて以前のLessonで作成したPostクラスにこのメソッドを使ってみましょう。結果を変数「newpost」に入れます。(クラスは大文字から書き始めるルールでしたね。)

$ rails c

irb> newpost = Post.new
=> #<Post id: nil, content: nil, created_at: nil, updated_at: nil>

以下のような結果が返ってきていると思います。

=> # <Post id: nil, content: nil, created_at: nil, updated_at: nil>

こちらがPostモデルの新規インスタンスです。
作成したカラムと同じ属性をハッシュ形式で持つデータだという事がわかりますね。
どんなテーブルを作成したか忘れてしまった人はMySQLクライアントを立ち上げて確認しましょう。

$ mysql -uroot -D board_development

mysql> desc posts;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| content    | varchar(255) | YES  |     | NULL    |                |
| created_at | datetime     | NO   |     | NULL    |                |
| updated_at | datetime     | NO   |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

それぞれの属性にアクセスするにはそのカラム名と同じ名前のインスタンスメソッドが使えるようになっています。以下操作を行ってみてください。

irb> newpost.content= "最初の投稿です。"
=> "最初の投稿です。"

Postモデルの新規インスタンスが入った変数newpostのcontentにアクセスし、「最初の投稿です」という値をセットしました。確認してみましょう。

irb> newpost
=> #<Post id: nil, content: "最初の投稿です。", created_at: nil, updated_at: nil>

contentに「最初の投稿です。」という文字が入ったのが確認できたと思います。

newメソッドまとめ

newメソッドは新規インスタンスを作成するメソッドになります。
また、モデルインスタンスははデータベースのカラム名と同じ属性を持ち、カラム名と同じ名前のインスタンスメソッドでアクセスできるという事を確認しておきましょう。

save

次に学習するのはモデルのインスタンスメソッド「save」です。saveメソッドはモデルをデータベースに保存するためのメソッドになります。
早速newpostを保存してみましょう。

irb> newpost.save
   (0.2ms)  BEGIN
  Post Create (0.4ms)  INSERT INTO `posts` (`content`, `created_at`, `updated_at`) VALUES ('最初の投稿です。', '20xx-xx-xx xx:xx:xx', '20xx-xx-xx xx:xx:xx')
   (7.4ms)  COMMIT
=> true

緑字の部分が発行されたSQLです。最後に=> trueが返ってきているのが確認できましたらsaveが成功したことになります。
saveが成功した後のnewpost変数を確認してみましょう。

irb> newpost
=> #<Post id: 1, content: "最初の投稿です。", created_at: "20xx-xx-xx xx:xx:xx", updated_at: "20xx-xx-xx xx:xx:xx">

先ほどnilだったid、created_at、updated_atに値が入っていますね。
これはsaveメソッド実行時にRailsアプリが自動的に値を入れてくれるようになっているためです。

idカラムはPrimaryKeyとして一意の(被らない)連番がint型で自動挿入されます。初期値は1です。

created_atは作成日が自動的に入ります。updated_atは更新日が自動的に入りますが、新規インスタンスを保存した時は作成日と同じ日付が入ることになります。

では本当に保存されたかMySQLクライアントに接続して確認しましょう。

$ mysql -D board_development

mysql> select * from posts;
+----+--------------------------+---------------------+---------------------+
| id | content                  | created_at          | updated_at          |
+----+--------------------------+---------------------+---------------------+
|  1 | 最初の投稿です。          | 20xx-xx-xx xx:xx:xx | 20xx-xx-xx xx:xx:xx |
+----+--------------------------+---------------------+---------------------+

さらにもう一つ投稿を作ってみましょう。

irb> post = Post.new(content: "二つ目の投稿です。")
irb> post.save

  Post Create (2.2ms)  I INSERT INTO `posts` (`content`, `created_at`, `updated_at`) VALUES ('二つ目の投稿です。','20xx-xx-xx xx:xx:xx', '20xx-xx-xx xx:xx:xx')

=> true

「true」が表示されましたら成功です。MySQLクライアントでもレコードが追加されているか確認してみてください。

create

createメソッドはsaveとよく似たメソッドになりますが、saveメソッドはインスタンスメソッドに対し、createはモデルクラスのメソッドになります。
そのため当然インスタンスには使えません。モデルクラスに対して使いましょう。
では以下のようにしてレコードを増やしましょう。

irb> Post.create(content: "hello3回目の投稿")

Post Create (0.4ms)  INSERT INTO `posts` (`content`, `created_at`, `updated_at`) VALUES ('hello3回目の投稿', '20xx-xx-xx xx:xx:xx', '20xx-xx-xx xx:xx:xx')

=> #<Post id: 3, content: "hello3回目の投稿", created_at: "20xx-xx-xx xx:xx:xx", updated_at: "20xx-xx-xx xx:xx:xx">

このようにcreateメソッドはモデルクラスから新規インスタンスを作成してレコードを保存するメソッドになります。saveメソッドとの違いをしっかり確認しておきましょう。
正しく保存されたかMySQLでも確認しておいてください。

all

allはモデルクラスのメソッドです。
レコード全てに対応するインスタンスを配列で返します。この配列のことをインスタンスの集合「collection」と呼びます。
早速使ってみましょう。(結果を変数postsに代入します。)

irb> posts = Post.all

  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` LIMIT 11
=> # "<ActiveRecord::Relation [
#<Post id: 1, content: "hello", created_at: "20xx-xx-xx xx:xx:xx", updated_at: "20xx-xx-xx xx:xx:xx">, 
#<Post id: 2, content: "二つ目の投稿です。", created_at: "20xx-xx-xx xx:xx:xx", updated_at: "20xx-xx-xx xx:xx:xx">, 
#<Post id: 3, content: "hello3回目の投稿", created_at: "20xx-xx-xx xx:xx:xx", updated_at: "20xx-xx-xx xx:xx:xx">
]>"

見やすくするために少し成形しています。今まで作成した3つのレコードが配列の形になっているのを確認しておきましょう。

id:1のインスタンスid:2のインスタンスid:3のインスタンス ]

Tips! なぜ変数に代入するのか

変数に代入すると、メモリ上にデータが展開され、次からは展開されたメモリ上のデータを参照するようになります。
変数に代入せずにPost.allを複数回実行すると、毎回データベースに問い合わせをすることになりますが、データベースにアクセスするよりメモリ上のデータを参照する方がパフォーマンスに優れています。そのため変数に代入しておき後からアクセスするための準備をしているのです。

Tips! rails cを見やすく

Railsコンソールの結果を見やすくするGem(ライブラリ)がいくつかあります。
hirbなどを使うとコンソールが見やすくなりますので興味がある方は使ってみてください。

find

find はidで検索するメソッドです。モデルインスタンスのコレクションにもモデルクラスにも使えます。

  • findメソッドはデータベースに問い合わせをします。
  • findメソッドはidで検索し、最初の1件のインスタンスを返します。
  • findで検索した結果何も無い場合はエラーが送出されます。

ではコレクションに使ってみましょう。

irb> posts = Post.all
irb> posts.find(1)
  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1
=> #<Post id: 1, content: "最初の投稿です。", created_at: "20xx-mm-dd HH:MM:SS", updated_at: "20xx-mm-dd HH:MM:SS">

idが1のPostモデルのインスタンスが返ってきましたね。
発行されているSQLにも注目しておきましょう。

SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 1 LIMIT 1

LIMIT 1」となっています。つまり、最初の1件だけ返ってくるという事です。

ではクラスにも使ってみましょう。

irb> Post.find(2)
  Post Load (1.0ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 2 LIMIT 1
=> #<Post id: 2, content: "hello3回目の投稿", created_at: "20xx-mm-dd HH:MM:SS", updated_at: "20xx-mm-dd HH:MM:SS">

idが2のインスタンスが返ってきましたね。

では存在しないidだった場合はどうでしょう。

irb> Post.find(100) 
  Post Load (0.7ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`id` = 100 LIMIT 1
Traceback (most recent call last):
        1: from (irb):12
ActiveRecord::RecordNotFound (Couldn't find Post with 'id'=100)

以下のような例外が送出されています。各自翻訳するなどしてエラーの概要を掴んでおきましょう。

ActiveRecord::RecordNotFound (Couldn't find Post with 'id'=100)

「idが100のレコードが見つからなかった」ということですね。このようにエラーが出ることをぜひ覚えておいてください。

また、以下のようにidの引数を渡さなかった場合、nilが渡された場合も確認しておきましょう。未定義のインスタンス変数はNilクラスのオブジェクトとなり、nilが返ります。

irb> Post.find()
Traceback (most recent call last):
        1: from (irb):13
ActiveRecord::RecordNotFound (Couldn't find Post without an ID)
irb> @id
=> nil
irb> Post.find(@id)
Traceback (most recent call last):
        1: from (irb):15
ActiveRecord::RecordNotFound (Couldn't find Post without an ID)

findはよく使うことになるメソッドです。見つからなかった場合はエラーとなることを理解しておきましょう。

find_by

findに似たメソッド「find_by」を学習していきましょう。
find_byメソッドは条件検索メソッドです。条件にある最初の1件を取得してインスタンスを返します。検索結果が無い場合はnilを返します。
早速操作していきましょう。

irb> posts = Post.all
  Post Load (18.9ms)  SELECT  `posts`.* FROM `posts` LIMIT 11
=>  #<ActiveRecord::Relation [#<Post id: 1, content: "最初の投稿です。", created_at: "20xx-mm-dd HH:MM:SS",
updated_at: "20xx-mm-dd HH:MM:SS">, #<Post id: 2, content: "hello3回目の投稿", created_at: "20xx-mm-dd HH:MM:SS", updated_at: "20xx-mm-dd HH:MM:SS">]>

find_byは条件指定になりますので、任意の属性(カラム)で検索することが可能です。例えばcontentが「最初の投稿です。」というものを検索してみます。文字列(string)を検索する場合はクォーテーションで括るのを忘れないようにしましょう。

irb> posts.find_by(content: "最初の投稿です。")
  Post Load (0.6ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`content` = '最初の投稿です。' LIMIT 1
=> # <Post id: 1, content: "最初の投稿です。", created_at: "20xx-mm-dd HH:MM:SS1", updated_at: "20xx-mm-dd HH:MM:SS">

無事一致するものが検索できましたね。ここでぜひ覚えておいてほしいのが、find_byの検索条件は完全一致だということです。
一文字でも違ってはいけません。「最初の投稿です」と最後の読点を無くすとnilが返ってくることを確認しておきましょう。

irb> posts.find_by(content: "最初の投稿です")
  Post Load (0.4ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`content` = '最初の投稿です' LIMIT 1
=> nil

モデルクラスに使う事もできます。

irb> Post.find_by(content: "最初の投稿です。")
  Post Load (0.6ms)  SELECT  `posts`.* FROM `posts` WHERE `posts`.`content` = '最初の投稿です。' LIMIT 1
=> # <Post id: 1, content: "最初の投稿です。", created_at: "20xx-mm-dd HH:MM:SS1", updated_at: "20xx-mm-dd HH:MM:SS">

find_byまとめ

  • find_byは検索条件の最初の1件を取得する。
  • 検索結果が無い場合はnilを返す。
  • 検索条件は完全一致。

主なモデル操作メソッド

メソッド 概要
new 新規オブジェクトの作成
save インスタンスの保存
create 新規インスタンスを作成して保存
all 全てのレコードのインスタンスの取得
find idで検索し、最初の1件のインスタンスを返す。検索結果が無い場合は「RecordNotFound エラー」を送出
find_by 条件検索し、最初の1件のインスタンスを返す。検索結果が無い場合はnilを返す。
where 条件検索し、インスタンスの配列で返す。