【Rails】findとfind_byとwhereと時々オトン
想定読者
- find, find_by, whereの使い分けがよくわからない人
findメソッドの概要
- 各モデルの
id
を検索キーとしてデータを取得するメソッド - id以外の条件で
検索不可
- 戻り値は
検索対象のクラスのインスタンス
- 取得したいデータのidの値が、1、10など
**特定されている場合**
に使用
# 単一のIDを指定
pry(main)> Customer.find(1)
Customer Load (0.4ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
# 複数のIDを指定
pry(main)> Customer.find(1, 2, 3)
Customer Load (8.7ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" IN ($1, $2, $3) [["id", 1], ["id", 2], ["id", 3]]
# 配列で複数のIDを指定
pry(main)> Customer.find([1, 2, 3])
Customer Load (3.5ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" IN ($1, $2, $3) [["id", 1], ["id", 2], ["id", 3]]
- エラー時の戻り値(検索対象が存在しなかった場合)は
例外を返す
- もう一度いう。検索対象が存在しなかった場合は
例外を返す
pry(main)> Customer.find(189879)
Customer Load (1.5ms) SELECT "customers".* FROM "customers" WHERE "customers"."id" = $1 LIMIT $2 [["id", 189879], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find Customer with 'id'=189879
find_byの概要
- 各モデルを
id以外の条件で
検索するメソッド(idでも検索可能) - 複数の検索条件を指定可能
- 戻り値は
検索対象のクラスのインスタンス
- 返ってくる結果は、最初にヒットした
1件のみ
id及びid以外の条件が分かっている場合
、その条件に該当する最初のデータを取得したい場合に使用
pry(main)> Customer.find_by(name: "株式会社ミライ・トラスト")
Customer Load (7.2ms) SELECT "customers".* FROM "customers" WHERE "customers"."name" = $1 LIMIT $2 [["name", "株式会社ミライ・トラスト"], ["LIMIT", 1]]
- エラー時の戻り値(検索対象が存在しなかった場合)は
nilを返す
pry(main)> Customer.find_by(name: "株式会社ミライトラスト")
Customer Load (34.7ms) SELECT "customers".* FROM "customers" WHERE "customers"."name" = $1 LIMIT $2 [["name", "株式会社ミライトラスト"], ["LIMIT", 1]]
=> nil
whereの概要
- 各モデルを
id以外の条件で
検索するメソッド - 該当するデータ全てが返ってくる
- 戻り値は
ActiveRecord::Relationオブジェクト
# 前方一致
pry(main)> Customer.where('address like ?', '東京都')
Customer Load (41.9ms) SELECT "customers".* FROM "customers" WHERE (address like '東京都')
# 後方一致
pry(main)> Customer.where('name like ?', '株式会社%')
Customer Load (71.4ms) SELECT "customers".* FROM "customers" WHERE (name like '株式会社%')
# 部分一致
pry(main)> Customer.where('name like ?', '%アイドマ%')
Customer Load (34.8ms) SELECT "customers".* FROM "customers" WHERE (name like '%アイドマ%')
# and検索
pry(main)> Customer.where('name like ?', '%アイドマ%').where('address like ?', '東京都')
Customer Load (34.8ms) SELECT "customers".* FROM "customers" WHERE (name like '%アイドマ%')
- エラー時の戻り値(検索対象が存在しなかった場合)も要素0個の
ActiveRecord::Relationオブジェクト
pry(main)> Customer.where('name like ?', '%アイドマーーー%')
Customer Load (92.5ms) SELECT "customers".* FROM "customers" WHERE (name like '%アイドマーーー%')
[]
まとめ
- リソースが特定されているCRUD処理では基本的に
find
を使用
→例えばリソースの更新時、削除時にfindbyでidを指定することで同様にリソースを検索することは可能だが、findbyの場合エラー時の戻り値はnilとなり、findのように適切な例外クラス(RecordNotFound)が上がらない。find_byでエラー時にnilが返ることにより、UndefindMethodやActionController::UrlGenerationErrorなど実際のエラー内容とは異なる例外が上がり、ログ監視ツール上でもHTTPステータスエラーとしては本来404で上がってくるべきところが500番台(InternalServerError)などが返されたりしてエラーの特定に混乱を招いたりもしかねない。
- リソースが特定されているCRUD処理のうち、findでうまく引っ掛けられない場合には
find_by
を使用(あまり想定できないが) - リソースが特定できていない、複数のリソースをまとめて取得したい場合は
where
を使用