※2022年から技術系の記事は個人ブログに投稿しております。ぜひこちらもご覧ください→yamaday0u Blog
プログラミングスクールの課題アプリ開発の過程で、findメソッドとfind_byメソッドを勘違いしてまる1日エラーに悩まされた経験から、findメソッドとfind_byメソッドについて整理します。
findメソッド
IDを指定してレコードを取得します。
指定したIDの中に存在しないIDが1つでもあると例外(エラー)が発生します。
Fruit.find(1)
# 複数のIDを指定して、該当するレコードを取得できる
Fruit.find(1,4,6)
# 配列でも指定できる
Fruit.find([1,4,6])
# 同一ファイル内にidの情報を含むインスタンスがあれば、以下のような書き方ができる
Fruit.find(@fruit.id)
find_byメソッド
条件を指定してヒットした最初の1件を取得します。IDも条件に指定できます。
条件にヒットするレコードが存在しない場合はnilを返します。
Fruit.find_by(name: "strawberry")
Fruitsテーブルに以下のようにデータが入っているとします。
Fruitsテーブル
| id | name | color |
| 1 | apple | red |
| 2 | strawberry | red |
| 3 | orange | orange |
| 4 | banana | yellow |
この場合、次のように記述して該当するデータを取得できます。
Fruit.find_by(name: "apple")
Fruit.find_by(name: "orange")
Fruit.find_by(color: "yellow")
個人的なミス経験で恐縮ですが、ぼくはfindメソッドとfind_byメソッドを混同して以下のように記述してエラーに悩まされていました。
Fruit.find(id: @fruit.id)
findメソッドを使っているのに、カッコ()の中はfind_byメソッドの文法で記述していました。
どう違うのか
Railsドキュメントを読んでみると、findメソッドはRailsのバージョン1.0.0から、find_byメソッドはRailsのバージョン4.0.2から適用されているので、歴史的にはfind_byメソッドの方が後から登場したメソッドのようです。
機能的には次のような違いがあります。
- findメソッドもfind_byメソッドもIDを指定してレコードを取得することができる
- findメソッドはIDを複数指定していれば複数の値を取得できるが、find_byメソッドは1つのレコードしか取得できない
- find_byメソッドはID以外の条件を指定してレコードを取得できる
結論としては、こんな感じでしょうか。
取得したいレコード | 使用するメソッド |
---|---|
1つ&IDを指定 | findメソッド・find_byメソッド |
複数&IDを指定 | findメソッドのみ |
1つ&ID以外を指定 | find_byメソッドのみ |
複数&ID以外を指定 | ??? |
最後のパターンで???が出ました。
このパターンは、findメソッドでもfind_byメソッドでも取得できません。
このときはどうすればよいでしょうか?
Whereメソッド
取得したいレコードが「複数&ID以外を指定」の場合はWhereメソッドを使用することで実行可能です。
whereメソッドは条件に当てはまる値を全て取得します。
@fruit = Fruit.where(name: "strawberry")
ここまでみるとwhereメソッドがなんでもできて最強じゃないかとも思いますが、findとfind_byメソッドにはできて、whereメソッドにはできないことがあります。
# findとfind_byメソッドで取得した値は、特定のカラムを取得できる
@fruit = Fruit.find(1) # もしくは、@fruit = Fruit.find_by(name: "strawberry")
@fruit.name
# 実行結果 => "strawberry"
# whereメソッドで取得した値は特定のカラムを取得できない
@fruit = Fruit.where(name: "strawberry")
@fruit.name
# 実行結果 => NoMethodErrorになる
findメソッドとfind_byメソッドはモデルのインスタンスを返すのに対し、whereメソッドは範囲をしぼるだけなので、インスタンスがありません。
そこで以下のような方法で、範囲を絞った中でどれを使うか指定することで特定のカラムを取得できるようになります。
@fruit = Fruit.where(name: "strawberry").first(1)
@fruit.name
# 実行結果 => "strawberry"
@fruit = Fruit.where(name: "strawberry").take(1)
@fruit.name
# 実行結果 => "strawberry"
@fruit = Fruit.where(name: "strawberry")
@fruit.each do |fruit|
fruit.name
end
# 実行結果 => "strawberry"
参考資料
Railsドキュメント モデルについて
Qiita【Rails初心者必見!】ひたすら丁寧にデータ取得を説明(find, where)
Qiita【Rails】findとfind_byの違い
Qiita【Rails】find・find_by・whereについてまとめてみた