見出し画像

完全理解!Rails find_byについて

find_byメソッドをなんとなく使ってる。
find_byメソッドの使い方がわからない。

そんな方へ向けて書きました!

今回は、Ruby on Rails(以下Rails)のfind_byメソッドについて、Rails初心者でもわかるように、具体的なソースコードを用いながら解説してみました。

私自身Railsを使って仕事を始めた際、find_byメソッドについて理解できるようになるまでかなり苦労しました。findとの違い使いどころが良くわからなかったことを今でも覚えてます・・・。今回は、find_byメソッドについてなるべく難しい用語を使わず、最低限必要な用語だけを用いて解説するよう試みました。
私と同じような苦労をしていらっしゃる方でも、本記事を読み終える頃には、Rails find_byメソッドについて完璧に理解できている状態になれます!

find_byメソッドについて

まずは、find_byに関する簡単なご説明です。

find_byは、データベースからデータを取得する際に使用するメソッドです。つまり、データベースに保存したデータの中から自分が欲しいデータを取得する際に用いるメソッドになります。
find_byはデータを1つだけ返します。そして、重要なのは、与えられた条件にマッチするレコードのうち最初のレコードだけを返すということです。

find_byメソッドの使い方

基本的にはfindメソッドと使い方は同じです。
findメソッドに関する解説記事は下記を参考にしてみてください。

findメソッドとの違い

1つ目の違いは、引数に指定できるカラムです。
findの場合は、引数に指定できるのは必ずidとなります。
そのため、下記のコード例のように値だけを引数に渡します。

User.find(1)

一方、find_by特定のカラムを指定して、データを取得することができます。例えば、下記のようにnameというカラムを指定して、userの名前でデータを取得することが可能です。

irb(main):001:0> User.find_by(name: 'test1')
  (1.3ms)  SELECT sqlite_version(*)
 User Load (0.5ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "test1"], ["LIMIT", 1]]
=> #<User id: 1, name: "test1", created_at: "2021-04-03 13:52:14.218816000 +0000", updated_at: "2021-04-03 13:52:14.218816000 +0000">

もちろんidカラムを指定することも可能です。

irb(main):002:0> User.find_by(id: 1)
 User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "test1", created_at: "2021-04-03 13:52:14.218816000 +0000", updated_at: "2021-04-03 13:52:14.218816000 +0000">

さらに、複数のカラムを指定することも可能です。

irb(main):002:0> Post.find_by(user_id: 1, name: 'post1')
 Post Load (0.5ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ? AND "posts"."name" = ? LIMIT ?  [["user_id", 1], ["name", "post1"], ["LIMIT", 1]]
=> #<Post id: 1, user_id: 1, name: "post1", created_at: "2021-04-03 13:52:14.223957000 +0000", updated_at: "2021-04-03 13:52:14.223957000 +0000">

ちなみに、引数にカラムを指定しなかった場合はエラーが起きます。

irb(main):009:0> User.find_by('test1')
 User Load (0.7ms)  SELECT "users".* FROM "users" WHERE (test1) LIMIT ?  [["LIMIT", 1]]
Traceback (most recent call last):
       2: from (irb):8
       1: from (irb):9:in `rescue in irb_binding'
ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: test1)
irb(main):010:0> User.find_by(1)
Traceback (most recent call last):
       2: from (irb):9
       1: from (irb):10:in `rescue in irb_binding'
ArgumentError (Unsupported argument type: 1 (Integer))

2つ目の違いは、取得しようと思ったデータがなかった場合の処理です。findの場合、引数に指定したidを持つレコードがテーブルに存在しなかった場合、ActiveRecord::RecordNotFoundというエラーが起きます。

irb(main):003:0> User.find(0)
 User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
Traceback (most recent call last):
       1: from (irb):3
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=0)

一方、find_byの場合、引数に指定した任意の値を持つレコードがテーブルに存在しなかった場合、nilが返ってきます。

irb(main):004:0> User.find_by(name: 'hoge')
 User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "hoge"], ["LIMIT", 1]]
=> nil

ただし、find_byの末尾に!をつけるとfindと同様にActiveRecord::RecordNotFoundというエラーを発生させることができます。

irb(main):003:0> User.find_by!(name: 'hoge')
 User Load (0.7ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "hoge"], ["LIMIT", 1]]
Traceback (most recent call last):
       1: from (irb):3
ActiveRecord::RecordNotFound (Couldn't find User)

Railsが用意した面白い機能としてテーブルに定義したフィールド(カラム)に対して動的にメソッドを提供するというものがあります。文だけだと難しいですが、コード例を見ていただけるとわかるかと思います。
例えば、usersテーブルにnameというカラムがある場合、下記のようなメソッドが自動的に定義されます。

irb(main):004:0> User.find_by_name('test1')
 User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "test1"], ["LIMIT", 1]]
=> #<User id: 1, name: "test1", created_at: "2021-04-03 13:52:14.218816000 +0000", updated_at: "2021-04-03 13:52:14.218816000 +0000">

これは、find_by_フィールド名 というメソッドが自動的に定義されるということになります。もちろん、find_by_idというメソッドも定義されます。

irb(main):005:0> User.find_by_id(1)
 User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "test1", created_at: "2021-04-03 13:52:14.218816000 +0000", updated_at: "2021-04-03 13:52:14.218816000 +0000">

この自動的に定義されるメソッドはfind_byと同様に、データがなかった場合にnilを返します。

irb(main):006:0> User.find_by_id(0)
 User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
=> nil

また、この自動的に定義されるメソッドも、文末に!をつけると、ActiveRecord::RecordNotFoundというエラーを発生させることができます。

irb(main):007:0> User.find_by_id!(0)
 User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 0], ["LIMIT", 1]]
Traceback (most recent call last):
       1: from (irb):7
ActiveRecord::RecordNotFound (Couldn't find User)

findとfind_byの違いまとめ

・findはidのみで検索する
・find_byは引数に指定したカラムで検索する
・findはデータがなかった場合エラーが起きる
・find_byはデータがなかった場合nilが返ってくる

findとfind_byの使い分け

findとfind_byの違いがそのまま使い分けのポイントとなります。

idで検索を行う場合、基本的にはfindを使う。
プログラムの要件によってはfind_byを使う。

id以外で検索を行いたい場合、find_byを使う。

今回は以上となります。

読んでいただき、ありがとうございました!

ぜひ、ツイッターなどで、感想などをツイートしていただけると幸いです!


この記事が気に入ったらサポートをしてみませんか?