find vs find_by vs where
Asked Answered
K

13

182

I am new to rails. What I see that there are a lot of ways to find a record:

  1. find_by_<columnname>(<columnvalue>)
  2. find(:first, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>).first

And it looks like all of them end up generating exactly the same SQL. Also, I believe the same is true for finding multiple records:

  1. find_all_by_<columnname>(<columnvalue>)
  2. find(:all, :conditions => { <columnname> => <columnvalue> }
  3. where(<columnname> => <columnvalue>)

Is there a rule of thumb or recommendation on which one to use?

Kirman answered 22/6, 2012 at 17:58 Comment(0)
S
138

Edit: This answer is very old and other, better answers have come up since this post was made. I'd advise looking at the one posted below by @Hossam Khamis for more details.

Use whichever one you feel suits your needs best.

The find method is usually used to retrieve a row by ID:

Model.find(1)

It's worth noting that find will throw an exception if the item is not found by the attribute that you supply. Use where (as described below, which will return an empty array if the attribute is not found) to avoid an exception being thrown.

Other uses of find are usually replaced with things like this:

Model.all
Model.first

find_by is used as a helper when you're searching for information within a column, and it maps to such with naming conventions. For instance, if you have a column named name in your database, you'd use the following syntax:

Model.find_by(name: "Bob")

.where is more of a catch all that lets you use a bit more complex logic for when the conventional helpers won't do, and it returns an array of items that match your conditions (or an empty array otherwise).

Synthesize answered 22/6, 2012 at 17:58 Comment(7)
find_by is not deprecated, but the syntax is changing a bit. From find_by_name("Bob") to find_by(:name, "Bob").Hemidemisemiquaver
@BrianMorearty I believe you meant find_by(name: "Bob")Darlleen
@BrianMorearty I couldn't find any evidence of find_by_... being deprecated, do you have a source? It seems find_by and find_by_... are both still supported in Rails 4.Erythropoiesis
@Dennis, you are correct that it's not deprecated, and that's what I said. But I could have been more clear when I said "but the syntax is changing a bit." What I meant was, "but a new syntax is available now as well." See MCB's correction for the new syntax.Hemidemisemiquaver
This is what mentioned in the rails 4.0 release "All dynamic methods except for find_by_... and find_by_...! are deprecated" more details of edgeguides.rubyonrails.org/…Twelvemo
While its not worth a downvote, I believe other answers are better qualifiers for an accepted answer. @hossam-khamis answer below includes very relevant information, which demonstrates that the use cases for these three types of queries are quite different. Its especially important to note that #find will raise while #where can return an empty Array/Relation.Flashboard
Would be great to add the last note (Edit) to the first line of the post. THanksPettifog
H
156

where returns ActiveRecord::Relation

Now take a look at find_by implementation:

def find_by
  where(*args).take
end

As you can see find_by is the same as where but it returns only one record. This method should be used for getting 1 record and where should be used for getting all records with some conditions.

Hagiology answered 3/4, 2014 at 9:49 Comment(2)
find_by returns an object, yet where returns a collection.Grieve
When query value out of range, find_by will rescue ::RangeError from where(*args) and return nil.Crossexamine
S
138

Edit: This answer is very old and other, better answers have come up since this post was made. I'd advise looking at the one posted below by @Hossam Khamis for more details.

Use whichever one you feel suits your needs best.

The find method is usually used to retrieve a row by ID:

Model.find(1)

It's worth noting that find will throw an exception if the item is not found by the attribute that you supply. Use where (as described below, which will return an empty array if the attribute is not found) to avoid an exception being thrown.

Other uses of find are usually replaced with things like this:

Model.all
Model.first

find_by is used as a helper when you're searching for information within a column, and it maps to such with naming conventions. For instance, if you have a column named name in your database, you'd use the following syntax:

Model.find_by(name: "Bob")

.where is more of a catch all that lets you use a bit more complex logic for when the conventional helpers won't do, and it returns an array of items that match your conditions (or an empty array otherwise).

Synthesize answered 22/6, 2012 at 17:58 Comment(7)
find_by is not deprecated, but the syntax is changing a bit. From find_by_name("Bob") to find_by(:name, "Bob").Hemidemisemiquaver
@BrianMorearty I believe you meant find_by(name: "Bob")Darlleen
@BrianMorearty I couldn't find any evidence of find_by_... being deprecated, do you have a source? It seems find_by and find_by_... are both still supported in Rails 4.Erythropoiesis
@Dennis, you are correct that it's not deprecated, and that's what I said. But I could have been more clear when I said "but the syntax is changing a bit." What I meant was, "but a new syntax is available now as well." See MCB's correction for the new syntax.Hemidemisemiquaver
This is what mentioned in the rails 4.0 release "All dynamic methods except for find_by_... and find_by_...! are deprecated" more details of edgeguides.rubyonrails.org/…Twelvemo
While its not worth a downvote, I believe other answers are better qualifiers for an accepted answer. @hossam-khamis answer below includes very relevant information, which demonstrates that the use cases for these three types of queries are quite different. Its especially important to note that #find will raise while #where can return an empty Array/Relation.Flashboard
Would be great to add the last note (Edit) to the first line of the post. THanksPettifog
D
75

Model.find

1- Parameter: ID of the object to find.

2- If found: It returns the object (One object only).

3- If not found: raises an ActiveRecord::RecordNotFound exception.

Model.find_by

1- Parameter: key/value

Example:

User.find_by name: 'John', email: '[email protected]'

2- If found: It returns the object.

3- If not found: returns nil.

Note: If you want it to raise ActiveRecord::RecordNotFound use find_by!

Model.where

1- Parameter: same as find_by

2- If found: It returns ActiveRecord::Relation containing one or more records matching the parameters.

3- If not found: It return an Empty ActiveRecord::Relation.

Disability answered 27/12, 2016 at 0:32 Comment(0)
S
38

There is a difference between find and find_by in that find will return an error if not found, whereas find_by will return null.

Sometimes it is easier to read if you have a method like find_by email: "haha", as opposed to .where(email: some_params).first.

Scutcheon answered 22/6, 2012 at 18:20 Comment(0)
S
18

Since Rails 4 you can do:

User.find_by(name: 'Bob')

which is the equivalent find_by_name in Rails 3.

Use #where when #find and #find_by are not enough.

Sundae answered 22/6, 2014 at 13:51 Comment(4)
Agis, I agree with you but I've been searching over the internet that why should we use find_by and not find_by_<column_name>. I need it to answer someone.Langford
Agis, do you have any sources to back up your claim that we should no longer be using find_by_name in Rails 4? As far as I know, it's not been deprecated.Erythropoiesis
Is there a simple way to do that but say the name has a wildcard in it so something like find_by(name: "Rob*")Hannover
@Erythropoiesis It's possible to use both, they are valid. I prefer the new syntax since the API is more intuitive IMHO. It's how I would design it myself :)Sundae
F
8

The accepted answer generally covers it all, but I'd like to add something, just incase you are planning to work with the model in a way like updating, and you are retrieving a single record(whose id you do not know), Then find_by is the way to go, because it retrieves the record and does not put it in an array

irb(main):037:0> @kit = Kit.find_by(number: "3456")
  Kit Load (0.9ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" = 
 '3456' LIMIT 1
=> #<Kit id: 1, number: "3456", created_at: "2015-05-12 06:10:56",   
updated_at: "2015-05-12 06:10:56", job_id: nil>

irb(main):038:0> @kit.update(job_id: 2)
(0.2ms)  BEGIN Kit Exists (0.4ms)  SELECT 1 AS one FROM "kits" WHERE  
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.5ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE  "kits"."id" = 
1  [["job_id", 2], ["updated_at", Tue, 12 May 2015 07:16:58 UTC +00:00]] 
(0.6ms)  COMMIT => true

but if you use where then you can not update it directly

irb(main):039:0> @kit = Kit.where(number: "3456")
Kit Load (1.2ms)  SELECT "kits".* FROM "kits" WHERE "kits"."number" =  
'3456' => #<ActiveRecord::Relation [#<Kit id: 1, number: "3456", 
created_at: "2015-05-12 06:10:56", updated_at: "2015-05-12 07:16:58", 
job_id: 2>]>

irb(main):040:0> @kit.update(job_id: 3)
ArgumentError: wrong number of arguments (1 for 2)

in such a case you would have to specify it like this

irb(main):043:0> @kit[0].update(job_id: 3)
(0.2ms)  BEGIN Kit Exists (0.6ms)  SELECT 1 AS one FROM "kits" WHERE 
("kits"."number" = '3456' AND "kits"."id" != 1) LIMIT 1 SQL (0.6ms)   
UPDATE "kits" SET "job_id" = $1, "updated_at" = $2 WHERE "kits"."id" = 1  
[["job_id", 3], ["updated_at", Tue, 12 May 2015 07:28:04 UTC +00:00]]
(0.5ms)  COMMIT => true
Faradize answered 12/5, 2015 at 7:36 Comment(2)
exactly what I was looking forMatias
@kit = Kit.where(number: "3456").first - This lets you update it directly and it's safer since it survived deprecationMatias
F
8

Apart from accepted answer, following is also valid

Model.find() can accept array of ids, and will return all records which matches. Model.find_by_id(123) also accept array but will only process first id value present in array

Model.find([1,2,3])
Model.find_by_id([1,2,3])
Fallonfallout answered 6/8, 2015 at 10:46 Comment(0)
D
8

The answers given so far are all OK.

However, one interesting difference is that Model.find searches by id; if found, it returns a Model object (just a single record) but throws an ActiveRecord::RecordNotFound otherwise.

Model.find_by is very similar to Model.find and lets you search any column or group of columns in your database but it returns nil if no record matches the search.

Model.where on the other hand returns a Model::ActiveRecord_Relation object which is just like an array containing all the records that match the search. If no record was found, it returns an empty Model::ActiveRecord_Relation object.

I hope these would help you in deciding which to use at any point in time.

Dinsdale answered 12/7, 2019 at 16:23 Comment(0)
N
7

Suppose I have a model User

  1. User.find(id)

Returns a row where primary key = id. The return type will be User object.

  1. User.find_by(email:"[email protected]")

Returns first row with matching attribute or email in this case. Return type will be User object again.

Note :- User.find_by(email: "[email protected]") is similar to User.find_by_email("[email protected]")

  1. User.where(project_id:1)

Returns all users in users table where attribute matches.

Here return type will be ActiveRecord::Relation object. ActiveRecord::Relation class includes Ruby's Enumerable module so you can use it's object like an array and traverse on it.

Namecalling answered 14/10, 2016 at 9:7 Comment(0)
B
5

Both #2s in your lists are being deprecated. You can still use find(params[:id]) though.

Generally, where() works in most situations.

Here's a great post: https://web.archive.org/web/20150206131559/http://m.onkey.org/active-record-query-interface

Billionaire answered 22/6, 2012 at 18:15 Comment(2)
great post doesn't exist.Maurist
web.archive.org/web/20150206131559/http://m.onkey.org/…Neurasthenia
S
1

Since Rails 4 you can do:

User.find_by(name: 'Bob')

which is the equivalent find_by_name in Rails 3.

Use #where when #find and #find_by are not enough.

Sinfonietta answered 15/3, 2023 at 9:12 Comment(0)
P
0

The best part of working with any open source technology is that you can inspect length and breadth of it. Checkout this link

find_by ~> Finds the first record matching the specified conditions. There is no implied ordering so if order matters, you should specify it yourself. If no record is found, returns nil.

find ~> Finds the first record matching the specified conditions , but if no record is found, it raises an exception but that is done deliberately.

Do checkout the above link, it has all the explanation and use cases for the following two functions.

Porphyry answered 6/3, 2020 at 4:21 Comment(0)
V
-6

I will personally recommend using

where(< columnname> => < columnvalue>)
Vada answered 3/5, 2017 at 11:41 Comment(1)
This may answer the question. But try describing the pros and cons of using your approach, instead of the accepted answer.Plough

© 2022 - 2025 — McMap. All rights reserved.