すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Rails

【Rails】 世界で一番詳しいfind_byメソッドのいろいろな使い方

ぴっかちゃん
ぴっかちゃん

find_byメソッドとは、検索でヒットしたレコードの初めの一件だけを返すメソッドです。

find_byメソッドの基本構文
1
モデル名.find_by(条件)

例えば、以下のコードを実行すると、usersテーブルから「ageカラムの値が25」という条件に合う最初のレコードを1件だけ返します。

find_byメソッドの使用例
1
2
User.find_by(age: 25)
SELECT `users`.* FROM `users` WHERE `users`.`age` = 25 LIMIT 1

find_byの動作確認動画

このように、find_byメソッドの引数に渡した条件に一致した最初のレコードを1件だけ返します。

find_byメソッドの使い方

この章では、find_byメソッドの使い方について1つ1つ丁寧に解説します。

find_byメソッドを使用する場面とは?

検索したいカラムに対して値をセットして実行すると、Userモデルのインスタンスが一件だけ返ってきます。

1
2
3
4
5
6
7
8
9
10
11
User.find_by(email: 'programan@gmail.com')

=> #<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>

find_byを使う箇所は下記の3点あります。

  1. 一件しかヒットしないレコードに対して使用
  2. unique制約がかかっているカラムに対して使用
  3. 完全一致以外での検索に使用(あいまい検索等、完全一致よりも使用頻度は少ない)

find_byは一件だけしか返ってこないので、単一カラムの検索であればunique制約をかけているカラムの検索に使うことが多いです。

あいまい検索

programanという文字が含まれている中で、誰でも良いからとりあえず一人のデータを見たいなという場合

1
2
3
4
5
6
7
8
9
10
11
User.find_by("name like '%programan%'")

=> #<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>

programanという文字が含まれているユーザーは他にもいますが、find_byでは複数件ヒットした中の初めの一件だけ返ってきてます。

不等号検索

あれ、今日ユーザー登録してくれたユーザーいたかな??という時にはこんな感じで検索します。

1
2
3
4
5
6
7
8
9
10
11
User.find_by('created_at > ?', Date.today)

=> #<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>

これでデータ返ってきてるんで、あ、今日ユーザー登録してくれた人いるな!という感じです。

もし登録してくれた人がいなかった場合はnilが返ってきます。

1
2
3
User.find_by('created_at > ?', Date.tomorrow)

=> nil

nilが返ってきたら登録してくれたユーザーはいなかったなと確かめられる訳です。

複数の条件で指定した場合

そもそも一件しかヒットしないレコードに対してfind_byを使うことが多いと言いましたが、それは複数の条件を指定した場合でも同じです。それが複合uniqueのカラムに対しての検索です。複合uniqueとは一つのカラムの場合はいっぱいデータあるけど、複数カラムの一致するデータは一件しかないよという場合です。

例えば今みなさんに見てもらっているピカわか!を例に考えてみましょう。
ピカわか!では、カテゴリー(ruby rails javascript等)ごとに記事が書かれています。


ピカわか!のblogsからifという記事を取り出したいときに
rubyにもifという記事はありますし、javascriptにもifという記事があります。
すると、下記のように先に作成されたrubyのifの記事が取得されます。
javascriptのifの記事は取得されません。(find_byはヒットしたレコードのうち初めの一件を返すメソッドであるからです。)

1
2
3
4
5
6
7
Blog.find_by(url_title: 'if')

=> #<Blog:0x007ff9172b9ba8
 id: 172,
 title: "if文を使っての条件分岐を徹底解説!",
 url_title: "if",
 category_id: 2(rubyのカテゴリー)

Blog.whereで複数出てくるか確認してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 Blog.where(url_title: 'if')

=> [#<Blog:0x007ff9172cc4d8
 id: 172,
 title: "if文を使っての条件分岐を徹底解説!",
 url_title: "if",
 category_id: 2(rubyのカテゴリー),
#<Blog:0x007ff9172cc398
  id: 1343,
  title: "javascriptのif",
  url_title: "if",
  panel_name: nil,
  panel_type_id: 52,
  category_id: 42(javascriptのカテゴリー)>]

上記のようにifに関して2つのレコードが取得されてしまいます。rubyのifの記事かjavascriptのifの記事かどちらか一つの記事を表示しなければいけないので、category_idとurl_titleを指定してそれぞれどのカテゴリーのif文のレコードを取得するように書く必要があります。

1
2
3
4
5
6
7
Blog.find_by(category_id: 2, url_title: 'if')

=> #<Blog:0x007ff9172b9ba8
 id: 172,
 title: "if文を使っての条件分岐を徹底解説!",
 url_title: "if",
 category_id: 2(rubyのカテゴリー)

1
2
3
4
5
6
7
8
9
Blog.find_by(category_id: 42, url_title: 'if')

<Blog:0x007ff9172cc398
  id: 1343,
  title: "javascriptのif",
  url_title: "if",
  panel_name: nil,
  panel_type_id: 52,
  category_id: 42(javascriptのカテゴリー)>

それぞれurl_titleをifでfind_byした場合には、どのカテゴリーのif文が取得できるかわからないが、category_idとurl_titleを指定することで想定した一件のblogを取得したい!このようなケースでfind_byを使うことがよくあります。

アソシエーションに対して使用する場合

単体モデルで使いがちですが、アソシエーションに対してfind_byを使うことも良くあります。

このアソシエーションで使うケースはよくあるのですが、例えばbefore_actionset_categoryみたいなメソッドを実行していて@categoryがある場合に使うことがあります。

イメージとしては、下記の様な形で使うことがあります。

1
2
3
4
5
6
7
8
9
before_action :set_category

def show
  @blog = @category.blogs.find_by(url_title: 'if')
end

def set_category
  @category = Category.find(params[:category_id])
end

このparams[:category_id]に2(rubyのカテゴリー)が入っていたら、@categoryにはjavascriptのcategoryのインスタンスが入っているので、javascriptのifの記事が@blogに入る形です。

アソシエーションについては、「アソシエーションを図解形式で理解する」を参考にしてください。

カラムが存在しない、ヒットしたものがない、例外を発生させたい場合

find_byメソッドを実行して該当のデータが無かったり、カラム名を間違えていたり、例外を発生させたい場合、どのように記述すれば良いのでしょうか?

検索にヒットしたものがなかった場合

find_byでヒットしたものがなかった場合はnilが返ってきます。

1
2
3
Blog.find_by(url_title: 'ifhogehoge')

=> nil

該当のカラムが存在しない場合

該当のカラムが存在しない場合は、エラーが返ってきます。

1
2
Blog.find_by(url_titleaaa: 'if')
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'blogs.url_titleaaa' in 'where clause'

例外を発生させたい場合

レコードが絶対見つかる場合を想定しているのに、万が一レコードが見つからない場合は想定外のrequestを送ってきていると考えられ、例外処理でエラーページを表示したい時があるとします。
そういうときはfind_byではなく、find_by!を使います。

通常のfind_byだとヒットしたものがなかった場合はnilが返ってくるが、find_by!を使うと例外を発生させられることが出来ます。

1
2
3
Blog.find_by!(url_title: 'hogehoge')

ActiveRecord::RecordNotFound: Couldn't find Blog

他のメソッドとの違い

この章では、findメソッドやwhereメソッドとの違いについて解説します。

findメソッドとの違い

find_byメソッドはid以外の検索で使用するのに対し、findメソッドはidのみしか検索できません。返り値は単数ならインスタンスが返ってきます。

1
2
3
4
5
6
7
8
9
10
11
User.find(1)

=> #<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>

id以外で検索した場合

もしid以外で検索するとエラーが出てしまいます。

1
2
3
User.find(name: 1)

ActiveRecord::RecordNotFound: Couldn't find User with 'id'={:name=>1}

配列で指定した場合

単数ならインスタンスで返ってきますが、配列の場合だと配列で返ってきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
User.find([1,2])

=> [#<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>,
  id: 2,
  email: "programan_father@gmail.com",
  name: "programan_father",
  job_id: 2,
  sex: 0,
  age: 58,
  tall: 173.0,
  weight: 60.0,
  created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
  updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]

find_byでid検索した場合

1
2
3
4
5
6
7
8
9
10
11
User.find_by(id: 1)

=> #<User:0x007fe080f0ecf0
 id: 1,
 email: "programan@gmail.com",
 name: "programan",
 job_id: 1,
 sex: 0,
 age: 25, tall: 175.0, weight: 68.0,
 created_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00,
 updated_at: Fri, 14 Dec 2018 01:21:17 UTC +00:00>

インスタンスが返ってきます。findと一緒です。ただfindはid検索専用のメソッドなので、わざわざfind_byを使うことはないです。基本的にidの検索の場合はfindメソッドを使用して、id以外の検索の場合でuniqueなレコードを取得したい際にfind_byメソッドを使用します。

findメソッドの詳しい具体的な使い方はfindメソッドを徹底解説をご覧ください。

whereメソッドとの違い

find_byメソッドとwhereメソッドの一番の違いは、find_byメソッドの返り値がインスタンスであるのに対し、whereメソッドの返り値は配列であるということです。返り値がどう違うか見ましょう。

find_byメソッドとの返り値の違い

1
2
3
4
5
6
7
8
9
10
11
12
13
User.where(name: 'programan')

=> [#<User:0x007fe07e36c480 
id: 1, 
email: "programan@gmail.com", 
name: "programan", 
job_id: 1, 
sex: 0,
age: 25,
tall: 175.0,
weight: 68.0,
created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]

一件しかヒットしなくても配列が返ってきます。

メソッドチェーンでfind_byメソッドを使う場合

whereの返り値に対してチェーンしてfind_byメソッドを書くことも出来ます。
その場合はand検索になります。

下記のサンプルコードの場合だとnameカラムがprogramanであるかつageが25のレコードを一件取得するというふうになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
User.where(name: 'programan').find_by(age: 25)

=> [#<User:0x007fe07e36c480 
id: 1, 
email: "programan@gmail.com", 
name: "programan", 
job_id: 1, 
sex: 0,
age: 25,
tall: 175.0,
weight: 68.0,
created_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00,
updated_at: Fri, 19 Jul 2019 11:24:34 UTC +00:00>]

whereメソッドの詳しい具体的な使い方はwhereメソッドを徹底解説をご覧ください。

この記事のまとめ

  • find_byメソッドは、検索条件にヒットした初めの一件を返すメソッドのこと
  • uniqueなカラムや複合uniqueなカラムに対して使用する