Ruby
Rails

find、find_by、whereの違い

しっかりと、どう違うという説明ができなかったので、まとめました。

findメソッドとは

各モデルのidを検索キーとしてデータを取得するメソッド
id以外の条件で検索不可
取得したいデータのidの値が、1、10と具体的に分かっている場合に使用する。

(例)Aiueoモデルからid=1のデータを取得したい場合

irb(main):001:0> Aiueo.find(1)

  Aiueo Load (0.9ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

該当データがない場合は、ActiveRecord::RecordNotFoundが返ってくる。
つまり、エラーが返ってくるということ。

irb(main):006:0> Aiueo.find(1000000)
  Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."id" = $1 LIMIT $2  [["id", 1000000], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find Aiueo with 'id'=1000000

find_byメソッドとは

各モデルをid以外の条件で検索するメソッド(idでも検索可能)
複数の検索条件を指定可能
返ってくる結果は、最初にヒットした1件のみ
id及びid以外の条件が分かっている場合、その条件に該当する最初のデータを取得したい場合に使用する。

(例)Aiueoモデルからタイトルが「タイトル0」のデータを取得する場合

irb(main):004:0> Aiueo.find_by(title: "タイトル0")
  Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

該当データがない場合は、nilが返ってくる。

irb(main):007:0> Aiueo.find_by(title: "タイトル")
  Aiueo Load (0.6ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル"], ["LIMIT", 1]]
=> nil

whereメソッド

各モデルをid以外の条件で検索する場合
該当するデータ全てが返ってくる。

(例)Aiueoモデルからタイトルが「タイトル0」のデータを取得する場合

irb(main):005:0> Aiueo.where(title: "タイトル0")
  Aiueo Load (1.0ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">]>

find_byの結果と違い、先頭にActiveRecord::Relationの文字が。
ActiveRecord::Relation は、検索クエリを組み立てるもので、検索結果のオブジェクトとは異なるものである、とのこと。

う〜ん、むずかしい・・・

findfind_byメソッドは、検索結果のインスタンス(オブジェクト)(簡単にいうと、検索結果のデータがつまったもの)を返すが、
whereメソッドは、検索結果をオブジェクトとして返しているのではなく、検索するためのプログラムを発生させ、結果を表示しているだけの状態(うまく説明できない・・・)

なので、whereでは、取得したデータから特定カラムのデータを取得しようとするとエラーになる。

irb(main):012:0> @aiueo = Aiueo.where(title: "タイトル0")
  Aiueo Load (0.6ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">]>

irb(main):013:0> @aiueo.title
NoMethodError:   Aiueo Load (0.4ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 11]]
undefined method `title' for #<Aiueo::ActiveRecord_Relation:0x007fef48b8c3a8>

一方、find_byでは、実行結果のインスタンスが返ってきているので、特定カラムのデータも取得できる。

irb(main):014:0> @aiueo = Aiueo.find_by(title: "タイトル0")
  Aiueo Load (0.9ms)  SELECT  "aiueos".* FROM "aiueos" WHERE "aiueos"."title" = $1 LIMIT $2  [["title", "タイトル0"], ["LIMIT", 1]]
=> #<Aiueo id: 1, name: "名前0", title: "タイトル0", created_at: "2018-03-13 04:39:22", updated_at: "2018-03-13 04:39:22">

irb(main):015:0> @aiueo.title
=> "タイトル0"

まとめ

使用区分としては、

idの値が分かっていて、そのidのデータを取得したい場合・・・find
idの値が不明で、id以外のカラムを検索条件としたい場合・・・find_by
id以外のカラムの検索条件で、複数の実行結果を取得したい場合・・・where

こんなところかなと。