【Rails】find_by_idメソッドの使い方と他のfind系メソッドとの違い

Rails

find_by_idメソッドとは、引数に指定したidのレコードを1件だけ返すメソッドです。

find_by_idの構文
1
モデル名.find_by_id(取得したいレコードのid)

Userモデルのid=3のインスタンスを取得したい場合は、以下のように、find_by_idメソッドの引数に3を指定する事で取得することが出来ます。

コンソール | Userインスタンスの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テーブルのレコード)に次のデータが既に存在しているものとします。

shell
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">]

usersテーブルの中身

このデータを使ってfind_by_idメソッドの挙動を確認します。

基本的な使い方
リンクをコピーしました

冒頭でも解説していますが、モデル名.find_by_id(取得したいid)で指定したidのモデルのインスタンスを取得することが出来ます。

以下のusersテーブルにあるid=4のレコードを取得したいとします。

id=4のモデルのレコードを取得したい

この場合は、以下のようにfind_by_idメソッドの引数に4を指定して実行すると、id=4Userモデルのインスタンスが一件だけ返ってきます。

コンソール | Userインスタンスのid=4のレコードを取得する
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!メソッドで実行すると例外を発生させることが出来ます。

コンソール | 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モデルのインスタンスのみです。

コンソール | find_by_idメソッドに引数を渡した場合
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のレコードを取得する事が出来ます。

コンソール | find_by_idメソッドとfindメソッドで同じインスタンスを取得する
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つのメソッドで取得して挙動の違いを確認していきましょう。

usersテーブルの中身

まずは、findメソッドにid=2を指定して実行すると、ActiveRecord::RecordNotFoundのエラーが発生する事を確認出来ます。

コンソール | id=2のレコードをfindメソッドで取得する
1
2
[1] pry(main)>User.find(2)
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2

そして、find_by_idメソッドにid=2を指定して実行すると、nilが返っているのが分かります。

コンソール | id=2のレコードをfind_by_idメソッドで取得する
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件のみ取得していました。

コンソール | find_by_idメソッドの引数に配列を渡した場合
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]を渡した場合は、以下のように全てのレコードを取得する事が出来ています。

コンソール | findメソッドの引数に配列を渡した場合
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_カラム名(条件)で、条件に一致したカラムのレコードを取得する事が出来ます。

find_by_XXX系の構文
1
モデル名.find_by_カラム名(条件)

例えば、nicknameカラムの値がひよこのレコードを取得したい場合は、find_by_カラム名のカラム名にはnicknameを入れて、引数の条件には文字列のひよこを指定して実行すると、以下のような結果になります。

コンソール | find_by_nicknameメソッドで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のレコードを取得します。

コンソール | find_by_nicknameメソッドでnicknameカラムの値がひよこのレコードを取得
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が渡されます。

この流れを確認していきましょう。
まずは、ルーティングに以下を追加します。

config/routes.rb | /: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アクションに以下のように記述します。

app/controllers/users_controller.rb | 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を入力します。

URLにニックネームを追加

このパラメーターは、上記のコードの@user = User.find_by_nickname(params[:nickname])params[:nickname]に入り、nicknamepikaの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(カラム名: 条件)の様に、条件に一致したカラムのレコードを取得する事が出来ます。

コンソール | find_by_idメソッドでnicknameカラムの値がひよこのレコードを取得
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が返る
まとめ
  • find_by_idメソッドとは、引数に指定したidのレコードを1件だけ返すメソッドです。
  • レコードが存在しなかった場合は、nilが返ります。
  • find_by_id!メソッドは、レコードが存在しなかった場合は例外を発生させます。