124

In my current rails program when I use something like

 user = User.find(10)

When there is no user with ID=10 , I will have exception like :

ActiveRecord::RecordNotFound: Couldn't find User with ID=10

Can I get nil instead of raising exception so when I do something like :

unless user = Challenge.find(10)
  puts "some error msg"         
end

I just want to get nil when there is no records and I don't want to use begin/rescue

Thanks

Kevin
  • 14,655
  • 24
  • 74
  • 124
Eqbal
  • 1,819
  • 2
  • 16
  • 25
  • 1
    possible duplicate of [Model.find(1) gives ActiveRecord error when id 1 does not exist](http://stackoverflow.com/questions/4966430/model-find1-gives-activerecord-error-when-id-1-does-not-exist) – Tobias Cohen Jan 21 '14 at 22:32

8 Answers8

217

Yes, just do:

Challenge.find_by_id(10)

For Rails 4 and 5:

Challenge.find_by(id: 10)
apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • 16
    odd! I would've never guessed that the `.find_by_*` would return nil and the `.find` wouldn't. – ddavison Aug 07 '14 at 18:25
  • This has changed in rails 4, see this answer http://stackoverflow.com/a/26885027/1438478 for the new way to find an item by a specific attribute. – Fralcon Mar 26 '15 at 20:58
  • I found a weird issue with Rails 4.2 where when you pass a hash as 'x' to `Something.find_by(id: x)` it would create a SQL statement with all the attribute/value pairs of the hash as part of the WHERE clause. Looks like a Rails bug to me. – Tilo May 24 '17 at 21:44
  • Rails (3, 4 or 5) generates dynamic `find_by_...` finders for every attribute a model has including `:id`. So, `Challenge.find_by_id(10)` should work regardless of Rails version. – Arta Jul 09 '17 at 18:34
  • As specified by @MohamedIbrahim below, you can also do: `Challenge.find(10) rescue nil` – Hallgeir Wilhelmsen Feb 15 '19 at 22:03
36

In Rails 4, dynamic finders - such as find_by_id which was used in the accepted answer - were deprecated.

Moving forward, you should use the new syntax:

Challenge.find_by id: 10
hattila91
  • 673
  • 7
  • 16
16

you can do this a bit hackish, just use the ActiveRecord Query Interface.

this will return nil, instead of raising a Exception

  User.where(:id => 10).first
beanie
  • 1,428
  • 9
  • 14
  • A reason to use this rather than `find_by_id` is that it's portable from Rails 3 to 4. In rails 4, it's `find_by(:id => 10)`. – Gene Jun 09 '14 at 23:42
4

Why don't you simply catch the exception? Your case looks exactly like what exceptions were made for:

begin
  user = User.find(10)
rescue ActiveRecord::RecordNotFound
  puts "some error msg"
end

If you want to recover from the error in the rescue block (e.g. by setting a placeholder user (null pattern)), you can continue with your code below this block. Otherwise you might just put all your code for the "happy case" in the block between "begin" and "rescue".

morgler
  • 1,669
  • 1
  • 18
  • 26
  • Btw: you don't even need the `begin…end` block, if you already have a block such as a controller method. In that case the only extra line you need is the `rescue` line. Much more elegant and easier to handle than checking for `nil` with an `if` statement. – morgler Aug 01 '17 at 07:30
4

For those struggling with mongoid, it turns out that both find and find_by methods will raise exception - no matter your rails version!

There is an option (namely raise_not_found_error) which can be set to false, but when falsey makes find method doesn't to raise exception as well.

Thus the solution for mongoid users is the disgusting code:

User.where(id: 'your_id').first # argghhh
Cristiano Mendonça
  • 1,220
  • 1
  • 10
  • 21
4

You can try this Challenge.exists?(10)

tonymarschall
  • 3,862
  • 3
  • 29
  • 52
0

You can use find_by with the required attribute (in your case the id) this will return nil instead of giving an error if the given id is not found.

user = Challenge.find_by_id(id_value)

or you could use the new format:

user = Challenge.find_by id: id_value

You could also use where but you have to know that where return an active record relation with zero or more records you need to use first to return only one record or nil in case zero records return.

user = Challenge.where(id: id_value).first
Alanoud Just
  • 317
  • 3
  • 5
-1

just as simple as:

user = User.find(10) rescue nil
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
  • 2
    I would rather be more specific about what error is expected to be rescued, ActiveRecord::RecordNotFound in this case. As pointed on [this answer](https://stackoverflow.com/a/35115377/2112183) by @morgler – luizrogeriocn Jun 19 '17 at 20:02
  • 2
    I personally find this to be the best answer. I'm already telling my code what to do if the user isn't found in `if user...else` – Wylliam Judd Jan 22 '18 at 22:07
  • 2
    This is bad practice, as this will swallow all exceptions, not only the "not found one", making your code less reliable. – Cristik Jun 06 '21 at 06:05