findメソッドとwhereメソッドの違い(〜 ActiveRecord::Record基礎 〜)

findメソッド

引数に指定した主キー に対応するオブジェクトを取り出すメソッド

返り値はモデルオブジェクト又は配列

マッチするレコードが見つからない場合、ActiveRecord::RecordNotFound例外が発生

whereメソッド

whereメソッドとは、条件を絞ることでデータベースから返されるレコードを制限するメソッド(取り出すわけではない)
条件は、文字列、配列、ハッシュのいずれかの方法で与える

# 文字列で指定
User.where("name = 'ポッポ'")
User.where("name = ?",'ポッポ')  #SQLインジェクション対策
User.where('name LIKE ?', "%ポ%")
User.where('name LIKE ?', "%#{sanitize_sql_like("")}%")  #SQLインジェクション対策

# 配列で指定
User.where("name = ?", params[:username])

# ハッシュで指定
User.where(name: "ポッポ")


whereメソッドの返り値はActiveRecord::Relation

ActiveRecord::Relationはメソッドチェーンできる。これで検索条件を組み立てていく。なのでActiveRecord::Relation自体は検索結果を表してはいない。 検索結果を取得したいならeachメソッド等で回す必要がある

whereメソッドで条件検索した結果、条件に合致するモデルが1つも無かった場合でも、返り値はActiveRecord::Relationオブジェクトになる。 中身が空っぽ(要素0個)のActiveRecord::Relationオブジェクトが返ってくる。

rails c

#Userにid:100が存在しない場合

users = User.where(id: 100)

users
#=> []
users.inspect
#=> "#<ActiveRecord::Relation []>"
users.class
#=> User::ActiveRecord_Relation

※ 配列が空の場合eachメソッドは実行されないから、usersにeachメソッドを使ってもエラーは出ない

findメソッドとwhereメソッドの違い

※ findメソッドは配列や単一のオブジェクトが返ってくるが、whereメソッドはActiveRecord::Relationオブジェクトが返ってくる


findメソッドの場合はメソッド実行時にクエリが発行されてデータベースから値を取ってくるが、

whereメソッドの場合はメソッド実行時にクエリは発行されない。つまり、whereを使うときはActiveRecord::Relationを返すメソッドをチェーンしていき条件を絞っていくような使い方をし、最後にeach文などでクエリを発行させデータベースから値を取得する

軽く確認してみる①

findメソッド(単数)

@user_find = User.find(1)

Image from Gyazo

※ 単一のオブジェクトが返ってくる(Userモデルのインスタンス

オブジェクトを確認してみる

@user_find.inspect

Image from Gyazo

クラスを確認してみる

@user_find.class
#=> User(id: integer, email: string, crypted_password: string, salt: string, created_at: datetime, updated_at: datetime, avatar: string, name: string)


findメソッド(複数)

@users_find = User.find(1, 2)

Image from Gyazo

※ モデルが入った配列になって返ってくる

オブジェクトを確認してみる

@users_find.inspect

Image from Gyazo

クラスを確認してみる

@users_find.class
#=> Array


whereメソッド(単数)

@user_where = User.where(id: 1)

Image from Gyazo

※ 中身1つだとしても配列の形で返ってくるが、これはActiveRecord::Relationオブジェクト

オブジェクトを確認してみる

@user_where.inspect

Image from Gyazo

クラスを確認してみる

@user_where.class
#=> User::ActiveRecord_Relation


whereメソッド(複数)

@users_where = User.where(id: [1, 2])

Image from Gyazo

findメソッドの返り値と見た目は全く一緒で配列になっているが、これはActiveRecord::Relationオブジェクト

オブジェクトを確認してみる

@users_where.inspect

Image from Gyazo

クラスを確認してみる

@users_where.class
#=> User::ActiveRecord_Relation


軽く確認してみる②

findメソッド

users_controller.rb

def show
    @user = User.find(params[:id])  #ここでSQL発行
end

SQLが発行された後にshow.html.slimがレンダリングされている つまり、findメソッドは、実行されるとその場でSQLを発行し、検索結果のオブジェクトをそのまま返す Image from Gyazo

@user
#=> #<User:0x00007fc5da95da88  id: 18, email: ogawa@ogawa,...,>

@user.class
#=> User(id: integer, email: string,...)

@user.email
#=> "ogawa@ogawa"


whereメソッド

users_controller.rb

def show
    @user = User.where(id: params[:id])  #ここでSQLは発行されない
end

一方でwhereメソッドを使った場合は、showアクションの処理が終了し、show.html.slimが呼び出されてからSQLが発行される。whereメソッドはActiveRecord::Relationオブジェクトを返し、ActiveRecord::Relation は検索クエリを組み立てるもの Image from Gyazo

ちなみに、whereメソッドがActiveRecord::Relationを返すことから、show.html.slimで= @user.emailと記述していると、「ActiveRecord::Relationではemailメソッドが定義されていない」というエラーが発生する

@user.email
#<NoMethodError: undefined method `email' for #<User::ActiveRecord_Relation:0x00007fc609411f50>>

ActiveRecord::Relationオブジェクトの場合、eachメソッドなら中身取り出せる。
eachメソッドは、テーブル全体を一度に取り出し、@usersの要素の数ぶんUserオブジェクトを作る。 なのでこの段階でSQLが発行される。

- @user.each do |user|
 = user.email
#=> "ogawa@ogawa"