Rails: Why does find(id) raise an exception in rails? [duplicate] - ruby-on-rails

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Model.find(1) gives ActiveRecord error when id 1 does not exist
If there is no user with an id of 1 in the database, trying User.find(1) will raise an exception.
Why is this?

Because that's the way the architects intended find(id) to work, as indicated in the RDoc:
Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). If no record can be found for all of the listed ids, then RecordNotFound will be raised.
If you don't want the exception to be raised, use find_by_id, which will return nil if it can't find an object with the specified id. Your example would then be User.find_by_id(1).

Further to runako's explanation, it's actually pretty useful to have the choice of whether an exception is raised or not. I'm working on a blog application and I wanted to add support for viewing the next or previous blog entry. I was able to add two instance methods to my Post model that simply return nil when you try to get the previous post when viewing the first post, or the next post when viewing the last post:
def next
Post.find_by_id(id + 1)
end
def previous
Post.find_by_id(id - 1)
end
This avoids my helper code which conditionally generates the Previous Post/Next Post links from having to handle the RecordNotFound exception, which would be bad because it would be using an exception for control flow.

Related

How can I get active record to throw an exception if there are too many records

Following the principle of fail-fast:
When querying the database where there should only ever be one record, I want an exception if .first() (first) encounters more than one record.
I see that there is a first! method that throws if there's less records than expected but I don't see anything for if there's two or more.
How can I get active record to fail early if there are more records than expected?
Is there a reason that active record doesn't work this way?
I'm used to C#'s Single() that will throw if two records are found.
Why would you expect activerecord's first method to fails if there are more than 1 record? it makes no sense for it to work that way.
You can define your own class method the count the records before getting the first one. Something like
def self.first_and_only!
raise "more than 1" if size > 1
first!
end
That will raise an error if there are more than 1 and also if there's no record at all. If there's one and only one it will return it.
It seems ActiveRecord has no methods like that. One useful method I found is one?, you can call it on an ActiveRecord::Relation object. You could do
users = User.where(name: "foo")
raise StandardError unless users.one?
and maybe define your own custom exception
If you care enough about queries performance, you have to avoid ActiveRecord::Relation's count, one?, none?, many?, any? etc, which spawns SQL select count(*) ... query.
So, your could use SQL limit like:
def self.single!
# Only one fast DB query
result = limit(2).to_a
# Array#many? not ActiveRecord::Calculations one
raise TooManySomthError if result.many?
# Array#first not ActiveRecord::FinderMethods one
result.first
end
Also, when you expect to get only one record, you have to use Relation's take instead of first. The last one is for really first record, and can produce useless SQL ORDER BY.
find_sole_by (Rails 7.0+)
Starting from Rails 7.0, there is a find_sole_by method:
Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no record is found. Raises ActiveRecord::SoleRecordExceeded if more than one record is found.
For example:
Product.find_sole_by(["price = %?", price])
Sources:
ActiveRecord::FinderMethods#find_sole_by.
Rails 7 adds ActiveRecord methods #sole and #find_sole_by.
Rails 7.0 adds ActiveRecord::FinderMethods 'sole' and 'find_sole_by'.

Rails .update_attribute not updating

I notice this question pops up a lot, but after trying several recommended solutions I found I still can't figure out what is wrong. I have a model called sample and a user model as well. When a sample is approved the hours on the sample are supposed to be added to the users total hours, but the users value is never updated. Each user has a unique email which is stored in the sample when it is submitted for approval. I checked in the database to make sure it wasn't an issue with accessing the value, and no error is being thrown so I am not really sure what is happening. I'm pretty new to ruby and rails so any help is appreciated. My samples_controller.rb contains the function:
def approve
#sample = Sample.find(params[:id])
#sample.update(sample_status:1)
#user = User.find(Sample.email)
hours_update = #user.hours + #sample.volunteer_hours
#user.update_attributes(:hours, hours_update)
redirect_to samples_adminsamples_path
end
Edit: thanks for the help everyone, turns out I needed to use the command
#user = User.find_by(email: #sample.email)
in order to get the proper user.
Can you please give some more data like db structure of Sample and User tables.
From the limited information, I think the line number 4 (#user = User.find(Sample.email)) is the problem.
.find() tries to query the DB on id and Sample.email would be giving user's email and not the id of the corresponding user in db.
I am also guessing that in your controller, you are suppressing the thrown exception some where using begin-rescue block because .find() throws ActiveRecord::RecordNotFound exception if it fails to find the resource.
Alternatively, if it is fetching the user correctly, you can also try update_column to update the values.
You are using incorrect format for update_attributes
It should be
#user.update_attributes(hours: hours_update)
or
#user.update_attribute(:hours, hours_update)
NOTE: update_attribute doesn't triggers the callbacks

Complex Rails app with new ActiveRecord::RecordNotFound error

I have adopted a Rails app that has a lot of complex relationships. We started getting ActiveRecord::RecordNotFound error and am trying to track it down.
Is there a way to wrap relationships in a begin ... rescue block to determine which one is causing us problems similar to what they are doing here for a find method? Or is there a way to log that exact SQL call that is getting the RecordNotFound error?
Edit
I am not able to find post I was referencing but I really just need to find the relationship that is busted. In my logs, I just see that it is rendering the template for 'not_found' records but I'm not sure what is causing it.
Without your relationships & logs, everything is conjecture; but if you're getting a RecordNotFound error - it basically means you're trying to load a record which doesn't exist in your db
Is there a way to wrap relationships in a begin ... rescue block to
determing which one is causing us problems similar to what they are
doing here for a find method
No - a better way to debug is to find the logs in your /log/development.log file & then display the response here.
A pointer is that I don't think the relationship will be the issue. The relationship will just return null if nothing is there; the RecordNotFound error will be a result of ActiveRecord not being able to find the requested resource
EG
#post = Post.find 13 #-> RecordNotFound if a post with id 13 does not exist
If you post your logs & code, it will be the best way to help us solve your issue!

Increment counter and rails "first" using postgreSQL strange behavior [duplicate]

This question already has answers here:
ActiveRecord Find All not sorting by ID?
(5 answers)
Closed 8 years ago.
When I increment some integer column using increment_counter and passing some record id and then try to get the first record using Model.first, this return the record id plus 1.
Something like this:
Model.increment_counter :field, id
Model.first
It returns not the
Model.find(1)
but
Model.find(id+1)
Is that some particular issue of postgreSQL?
Model.first will use the default sorting of your database (which is not necessarily an id).
Try this instead:
Model.order("id").first
You can do some monkey patching to ActiveRecord,
#lib/postgresql_extras.rb
module ActiveRecord
class Base
def self.first_by_id
order(:id).first
end
def self.all_by_id
order(:id)
end
end
end
and require this in some initializer
#config/initializer/extensions.rb
require "postgresql_extras"
don't call this ones first and all cause it will generate errors on other querys, for example User.order(:email).limit(1) it will be different from User.order(:email).first in this case, cause it will reorder by id the items,
I didn't find other methods with problems in posgresql yet and i try to fix it by change the tables pkey, but not luck there

Rails 2: Model.find(1) gives ActiveRecord error when id 1 does not exist

I am using Rails 2.3.5 and in that if I give Model.find(1) and if 1 is not in the database, it returns ActiveRecord error. Should it just be returning nil as in the case of Model.find_by_column('..')?
This is the expected behavior. I think David explains this the best himself, so here is a quote from Ruby, S., Thomas, D. & Hansson, D.H., 2009. Agile Web Development with Rails, Third Edition Third Edition., Pragmatic Bookshelf (p.330).
When you use a finder driven by
primary keys, you’re looking for a
particular record. You expect it to
exist. A call to Person.find(5) is
based on our knowledge of the people
table. We want the row with an id of
5. If this call is unsuccessful—if the record with the id of 5 has been
destroyed—we’re in an exceptional
situation. This mandates the raising
of an exception, so Rails raises
RecordNotFound.
On the other hand,
finders that use criteria to search
are looking for a match. So,
Person.find(:first,
:conditions=>"name=’Dave’") is the
equivalent of telling the database (as
a black box) “Give me the first person
row that has the name Dave.” This
exhibits a distinctly different
approach to retrieval; we’re not certain up front that we’ll get a result.
It’s entirely possible the result set
may be empty. Thus, returning nil in
the case of finders that search for
one row and an empty array for finders
that search for many rows is the
natural, nonexceptional response.
If you really don't want the exception, you can use find_by_id:
# #user = User.find(params[:id]) # original code
#user = User.find_by_id(params[:id])
if #user
# found!
else
# not found
end
This should be faster than a separate exists? check.
EDIT: Note that as #Miguelgraz commented, in Rails 4 you should instead say User.find_by(id: params[:id]). Same functionality, but now the implementation won't need method_missing.
throwing the exception is the expected behavior.
in fact in the normal course of events if you let the exception go unhandled your rails server will return the proper 404 page not found error.
if you'd like for it to return nil you can catch it yourself:
begin
#model = Model.find(id_provided)
rescue ActiveRecord::RecordNotFound => e
#model = nil
end
If you want the exception to be thrown in find_by_attributes flavors of the finder methods, you can use the bang! version of the method.
For example,
Model.find_by_category!(a_category_value)
will throw RecordNotFound if no match is found.
I found this to be DRY in scenarios like RESTful controllers, where I have a common error handler for the exception and I want my actions to behave consistently when a resource matching the given parameters is not found.
You can check if the record exists before fetching it.
#model = Model.find(id) if Model.exists?(id)
Rails 4 Method
if user = User.find_by(id: params[:id])
#do something with user
else
#throw error or redirect
raise ActiveRecord::RecordNotFound
end
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.
Model.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.
Model.where(id: id_value).first
You can simply use:
user = User.find(10) rescue nil

Resources