更新日:
【Rails】 find_by_idメソッドの使い方~他のfind系メソッドとの違いも
find_by_idメソッドとは、引数に指定したidのレコードを1件だけ返すメソッドです。
1
モデル名.find_by_id(取得したいレコードのid)
Userモデルのid=3
のインスタンスを取得したい場合は、以下のように、find_by_id
メソッドの引数に3
を指定する事で取得することが出来ます。
1
2
[1] pry(main)> User.find_by_id(3)
=> #<User:0x00007f902ff4bae8 id: 3, nickname: "ピカオ", age: 14, created_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, avatar: "1392782.png">
それでは、find_by_idメソッドの使い方について詳しく解説していきます。
find_by_idメソッドの使い方
この章では、find_by_id
メソッドの使い方や他のfind系メソッドとの違いについて解説して行きます。
使い方
sample_app
のアプリケーションでは、Userモデルのインスタンス(usersテーブルのレコード)に次のデータが既に存在しているものとします。
1
2
3
4
5
[1] pry(main)> User.all
=> [
#<User:0x00007f902fb06d98 id: 1, nickname: "ピカ子", age: 18, created_at: Tue, 10 Mar 2020 02:39:43 UTC +00:00, updated_at: Tue, 10 Mar 2020 03:00:52 UTC +00:00, avatar: "120185.png">,
#<User:0x00007f903091e070 id: 3, nickname: "ピカオ", age: 14, created_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, avatar: "1392782.png">,
#<User:0x00007f902f9ef658 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">]
このデータを使ってfind_by_id
メソッドの挙動を確認します。
基本的な使い方
冒頭でも解説していますが、モデル名.find_by_id(取得したいid)
で指定したidのモデルのインスタンスを取得することが出来ます。
以下のusersテーブルにあるid=4
のレコードを取得したいとします。
この場合は、以下のようにfind_by_id
メソッドの引数に4
を指定して実行すると、id=4
のUserモデルのインスタンスが一件だけ返ってきます。
1
2
[1] pry(main)> User.find_by_id(4)
=> #<User:0x00007f902fd9c1c0 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png"
そして、引数に指定したレコードがなかった場合は、nil が返ります。
1
2
[1] pry(main)>User.find_by_id(2)
=> nil
上記の引数に指定したid=2のレコードは存在しないので、nilが返ってきているのを確認する事が出来ました。
find_by_id!メソッド
find_by_id
メソッドは、レコードが存在しない場合はnilが返ると解説しましたが、!
を付けたfind_by_id!
メソッドで実行すると例外を発生させることが出来ます。
1
2
[1] pry(main)> User.find_by_id!(2)
ActiveRecord::RecordNotFound: Couldn't find User
上記を実行すると、レコードが存在しない時のエラーのActiveRecord::RecordNotFound
が発生します。
引数に複数指定
引数には、配列で複数指定することが可能ですが、取得することが出来るレコードは最初の1件だけになるので注意してください。
例えば、find_by_id
メソッドの引数に配列の[1, 3, 4]
を指定して実行すると、取得することが出来ているのはid=1
のUserモデルのインスタンスのみです。
1
2
3
4
[1] pry(main)> User.find_by_id([1, 3, 4])
SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4) LIMIT 1 # 発行されるSQL
=> #<User:0x00007f9030dd4d58 id: 1, nickname: "ピカ子", age: 18, created_at: Tue, 10 Mar 2020 02:39:43 UTC +00:00, updated_at: Tue, 10 Mar 2020 03:00:52 UTC +00:00, avatar: "120185.png">
これは、裏側で発行されているSQLのLIMIT 1
の箇所で取得するレコードを1つに制限しているからです。
findメソッドとの違い
find_by_id
メソッドと同様に、findメソッドでも引数に指定したidのレコードを取得する事が出来ます。
1
2
3
4
5
[1] pry(main)> User.find_by_id(4)
=> #<User:0x00007f902fd9c1c0 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png"
[2] pry(main)>User.find(4)
=> #<User:0x00007f9030a94418 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">
どちらのメソッドも引数に指定したidのレコードを取得する事が出来ています。
この2つのメソッドは、レコードがない場合と引数に配列を渡した場合の返り値が異なります。
レコードがなかった場合の挙動
レコードが無かった場合にfind_by_idメソッド
はnilが返りましたが、find
メソッドは例外が発生します。
find
メソッド - レコードがない場合は、例外が発生するfind_by_id
メソッド - レコードがない場合は、nil が返る
以下のusersテーブルは、id=2
のレコードが存在しません。この架空のレコードを上記の2つのメソッドで取得して挙動の違いを確認していきましょう。
まずは、find
メソッドにid=2
を指定して実行すると、ActiveRecord::RecordNotFound
のエラーが発生する事を確認出来ます。
1
2
[1] pry(main)>User.find(2)
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2
そして、find_by_idメソッドにid=2
を指定して実行すると、nil
が返っているのが分かります。
1
2
[1] pry(main)>User.find_by_id(2)
=> nil
レコードがなかった場合に、例外を発生させて例外処理でエラーページなどを表示したい場合は、find
メソッドやfind_by_id!
メソッドを使用しましょう。
配列を渡した場合の返り値
引数に配列を渡した場合にfind_by_id
メソッドは、最初の1件だけ取得出来ましたが、find
メソッドは、配列に指定されたレコードを全て取得する事が出来ます。
例えば、先ほどの引数に複数指定ではfind_by_id
メソッドの引数に[1,3,4]
を渡した場合は以下のように最初の1件のみ取得していました。
1
2
3
4
[1] pry(main)> User.find_by_id([1, 3, 4])
SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4) LIMIT 1 # 発行されるSQL
=> #<User:0x00007f9030dd4d58 id: 1, nickname: "ピカ子", age: 18, created_at: Tue, 10 Mar 2020 02:39:43 UTC +00:00, updated_at: Tue, 10 Mar 2020 03:00:52 UTC +00:00, avatar: "120185.png">
しかし、find
メソッドの引数に[1,3,4]
を渡した場合は、以下のように全てのレコードを取得する事が出来ています。
1
2
3
4
5
6
[1] pry(main)> User.find([1, 3, 4])
User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4)
=> [
#<User:0x00007f9030d2e458 id: 1, nickname: "ピカ子", age: 18, created_at: Tue, 10 Mar 2020 02:39:43 UTC +00:00, updated_at: Tue, 10 Mar 2020 03:00:52 UTC +00:00, avatar: "120185.png">,
#<User:0x00007f9030d2e318 id: 3, nickname: "ピカオ", age: 14, created_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, avatar: "1392782.png">,
#<User:0x00007f9030d2e1d8 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">]
この様にfind_by_id
メソッドは、配列を渡しても1件だけの取得でしたが、find
メソッドは配列に指定したidのレコードを全て取得する事が出来ます。
find_by_XXX系
Active Recordによって、自動的にテーブルのカラム名で検索出来るメソッドが与えられます。
find_by_id
メソッドは、引数に指定したidのレコードを取得する事が出来ましたが、以下のようにfind_by_カラム名(条件)
で、条件に一致したカラムのレコードを取得する事が出来ます。
1
モデル名.find_by_カラム名(条件)
例えば、nickname
カラムの値がひよこ
のレコードを取得したい場合は、find_by_カラム名
のカラム名にはnickname
を入れて、引数の条件には文字列のひよこを指定して実行すると、以下のような結果になります。
1
2
[1] pry(main)>User.find_by_nickname("ひよこ")
=> #<User:0x00007f9030dae248 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">
実行結果を確認すると、nickname= "ひよこ"
のレコードを取得する事が出来ています。
もし、id=5
のレコードにnickname= "ひよこ"
のレコードを追加したとしても、条件に一致した最初のレコードを取得するので、以下のようにid=4のレコードを取得します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# id=5にnicknameの値がひよこのレコードを追加
[1] pry(main)>User.create(nickname: "ひよこ", age: 18)
=> #<User:0x00007f9034802840 id: 5, nickname: "ひよこ", age: 18, created_at: Tue, 10 Mar 2020 15:54:12 UTC +00:00, updated_at: Tue, 10 Mar 2020 15:54:12 UTC +00:00, avatar: nil>
[2] pry(main)>User.all # id=4,id=5のnicknameカラムの値が"ひよこ"になっている事を確認
=> [#<User:0x00007f903074b478 id: 1, nickname: "ピカ子", age: 18, created_at: Tue, 10 Mar 2020 02:39:43 UTC +00:00, updated_at: Tue, 10 Mar 2020 03:00:52 UTC +00:00, avatar: "120185.png">,
#<User:0x00007f903074b310 id: 3, nickname: "ピカオ", age: 14, created_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:05 UTC +00:00, avatar: "1392782.png">,
#<User:0x00007f903074b1d0 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">,
#<User:0x00007f903074b090 id: 5, nickname: "ひよこ", age: 18, created_at: Tue, 10 Mar 2020 15:54:12 UTC +00:00, updated_at: Tue, 10 Mar 2020 15:54:12 UTC +00:00, avatar: nil>]
# nicknameがひよこの値のレコードをfind_by_nicknameで検索
[3] pry(main)>User.find_by_nickname("ひよこ")
=> #<User:0x00007f9030dae248 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">
# id=5のレコードではなく、id=4のレコードを取得している
find_by_id
メソッドは「カラム名」の部分にidを指定したのでidの検索をしました。
値がパラメーターで送られてくる場合
送られてくるパラメーターの内容が分かっていれば、find_by_XXX系の引数に渡して、そのパラメーターのユーザーのインスタンスを作成する事ができます。
例えば、/:nickname
というユーザー詳細ページのルーティングがあれば、このユーザー詳細ページにいくとparams[:nickname]
にnicknameが渡されます。
この流れを確認していきましょう。
まずは、ルーティングに以下を追加します。
1
2
3
Rails.application.routes.draw do
get '/:nickname' => 'users#nickname' # 追加
end
そして、新しくユーザーを作成します。
1
[1] pry(main)> User.create(nickname: "pika", age: 33)
usersコントローラのnicknameアクション
に以下のように記述します。
1
2
3
4
5
6
class UsersController < ApplicationController
def nickname
@user = User.find_by_nickname(params[:nickname])
binding.pry
end
end
これで以下のように、URLに先ほど作成したユーザーのnicknameを入力します。
このパラメーターは、上記のコードの@user = User.find_by_nickname(params[:nickname])
のparams[:nickname]
に入り、nickname
がpika
のUserのインスタンスを@user
に格納します。
コンソールで確認すると、以下のようにpika
のデータが@userに格納されています。
1
2
3
4
5
6
7
8
9
[1] pry(#<UsersController>)> @user
=> #<User:0x00007fd330857388
id: 8,
nickname: "pika",
age: 33,
created_at: Wed, 01 Apr 2020 02:35:45 UTC +00:00,
updated_at: Wed, 01 Apr 2020 02:35:45 UTC +00:00,
avatar: nil>
このように、パラメーターを使ってユーザーのインスタンスを作る事はよくある方法なので、覚えておきましょう。
find_byメソッド
find_by_カラム名(条件)
で、条件に一致したカラムのレコードを取得する事が出来ましたが、find_byメソッドでも以下のfind_by(カラム名: 条件)
の様に、条件に一致したカラムのレコードを取得する事が出来ます。
1
2
User.find_by(nickname: "ひよこ")
=> #<User:0x00007f902f329648 id: 4, nickname: "ひよこ", age: 22, created_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, updated_at: Tue, 10 Mar 2020 09:27:31 UTC +00:00, avatar: "120185.png">
この様に、find_by
メソッドやfind_by_カラム名
メソッドのどちらでも条件に一致するレコードを取得する事が出来ますが、一般的に多く使われているのはfind_by
メソッドです。
既存コードでfind_by_カラム名(条件)
が使われていたら、Active Recordによって付与された条件に一致するカラムのレコードを取得してくれるメソッドだと覚えておきましょう。
find系メソッド | 内容 | レコードが存在しなかった場合の挙動 |
---|---|---|
find | 引数に指定したidのレコードを返すメソッド 引数に配列を渡した場合→配列に指定したidの全てのレコード |
例外が発生する |
find_by_id | 引数に指定したidのレコードを1件だけ返すメソッド 引数に配列を渡した場合→最初の1件だけ |
nilが返る |
find_by | 検索でヒットしたレコードの最初の1件だけを返すメソッド | nilが返る |
find_by_カラム名 | Active Recordによって付与された条件に一致するカラムの最初のレコードを 取得してくれるメソッド |
nilが返る |
Active RecordやRuby on Railsのハイレベルな知識については、こちらの書籍でかなり詳しく解説されています。体系的に学びたい方はおすすめの良書です。
この記事のまとめ
- find_by_idメソッドは、引数に指定したidのレコードを1件だけ返すメソッドのこと
- レコードが存在しなかった場合は、nilが返る
- find_by_id!メソッドは、レコードが存在しなかった場合は例外を発生させる