Rails Controller Object.save - ruby-on-rails

I am working through Head First Rails and I came across some code that is confusing me a little. The goal of the code is to check and see if any error was made when a new record is being created. If there is an error, then the goal is the redisplay the page. If there is no error, the goal is to save the record to the database. Here is the controller code that the book gives:
def create
#ad = Ad.new(params[:ad])
if #ad.save
redirect_to "/ads/#{#ad.id}"
else
render :template => "ads/new"
end
end
The only thing about this code that confuses me is the fact the line (if #ad.save). Now, I know this line is testing to see if there are errors. If there are, it returns false, and if there are not, it returns true. However, I noticed that if no errors exist (it returns true), the record is actually saved. I thought "if" statments in ruby just tested a condition, but in this case, the condition is being tested AND executed. The strange this is that if I add another #ad.save, the database DOES NOT save the record twice. Like so:
def create
#ad = Ad.new(params[:ad])
if #ad.save
#ad.save
redirect_to "/ads/#{#ad.id}"
else
render :template => "ads/new"
end
end
This code does the exact same thing as the first bit of code. Why in the first bit of code is the #ad.save being executed, and how come on the second bit of code the #ad.save is not executed twice (only one record is created)?

Your assumption about if statements in ruby is incorrect. They can in fact execute code.
def return_true
puts 'inside method'
true
end
if return_true
puts "it ran some code"
end
# output will be:
# inside method
# it ran some code
In your second example the save is being run at least once. If the result of #as.save is truthy then it's run a second time. If it is going through the first part of the if branch then something else is preventing it from being saved to the database twice but there's not enough info for me to tell why. It could be that you have a unique contraint. Try doing #ad.save!. The bang version will throw an error if there are any validation errors.

Related

Why wont this work in my rails controller

I have this if condition that checks if the country is not in the USA
if params[:address][:country] != 'United States'
logger.info "I am in here"
#address.errors.add(:base, "We're sorry, you cant update your address")
#address.errors.add(:country, "We're sorry, you cant update your address")
end
if #address.update_attributes(params[:address])
flash[:notice] = 'Information was successfully updated.'
But I assumed that if you added an error to the base of an object it would halt but nothing is stopping the update and the flash notice always appears...How do i stop this and add a flash error instead
Randomly adding an error via #address.errors.add() will not cause the record to be invalid. That's why your call to update_attributes is still occurring (and not throwing a validation error). As far as I know, to function properly, errors.add() needs to happen inside the rails validation mechanism. Otherwise, you'll get some strange behavior.
For example, check this out in rails console
# assume a valid record
#address = Address.first
#address.valid? # => true
#address.errors.add(:random_error, "random error")
#address.errors # => {:random_error => ["random error"]}
#address.valid? # => true
#address.errors # => [] - call to valid? caused added error to disappear
You'd expect the call to valid? after adding an error to be false but it's not. And it also ends up clearing out the error you just added.
You said you can't move this to the model but that's really the right place for this. You can see more about active record validations here. Notice that all the examples have the validation happening in the models.

show error messages from two models when updating Rails

I am updating the attributes of two models from one form.
User.transaction do
begin
#user.update_attributes!(params[:user])
#board.update_attributes!(params[:board])
rescue ActiveRecord::RecordInvalid
end
end
When the #user.update_attributes produces an error the transaction is stopped and the error message is shown in the view.
However I want to try to update both #user and #board and get the error messages for both so that the user can correct all their mistakes one time.
How can I do this?
Thank you very much in advance.
All you need to do is not use the bang (!) version of update_attributes. Try:
User.transaction do
if #user.update_attributes(params[:user]) && #board.update_attributes(params[:board])
...do stuff...
else
render ... (go back to the action the user got here from)
end
end
Then in your view code put an errors block using a conditional like if #user.errors.any?.
The bang version of update_attribtues and most activerecord methods means it will raise an error if it fails, the regular version just returns false and doesn't save, so you can use that whenever it's ok to fail you just need to take action based on success/failure. Only use the bang version when it's not ok to fail, or when you want to show a 500 screen or something for some reason...
You could do the following:
User.transaction do
#user.update_attributes(params[:user])
#board.update_attributes(params[:board])
raise ActiveRecord::Rollback unless #user.valid? && #board.valid?
end
This will ensure that both update attribute methods run so that you can get error messages for both objects. If either object is invalid though no changes will be persisted to the database.
I think there is some mistake in the method name #user.update_attributes!(params[:user]) it should be like this #user.update_attributes(params[:user]) or once you need cross check your params values whether it is correct or not else your code looks correct. You can have a help with this update_attributes

Am i creating a nil object? (undefined method ___ for nil:NilClass) error - ruby on rails

My validations were working for a while, or so I thought. Now I come back to them after a while doing something else and I am getting the error above. I think it means I am creating a nil object with the .new method but I am just lost. It seemed to be a problem with the creation of a new object by the controller because even if I # out the validation the next validation in the tree on another attribute of #thing throws up the same error. However even if I create the object in the console and test it's there the .save method throws up the error still - undefined method 'user_id' for nil:NilClass
ThingsController:
def create
#thing = Thing.new(params[:thing])
#thing.user_id = #currentuser_id
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to #thing
else
#flash[:notice] = "Your thing did not get created."
render 'otherthings/show'
end
end
Thing.rb
validate :user_valid
def user_valid
errors.add("you must be logged in to add a thing") unless #thing.user_id?
end
I'm a bit of a ruby on rails noob (i.e. <8weeks) and this is my first stackoverflow question, so go easy on me if this is stupidly obvious. I've tried params["thing"] too, as that worked in the console after manually creating params to match the log files (whereas params [:thing] didn't) but that doesn't change anything in terms of the error message I get.
When you are calling unless #thing.user_id?, it's flipping out because there's no #thing (it doesn't get passed from your controller to your model as an instance variable).
I don't remember how exactly it works, but I'm pretty sure that when calling validate :user_valid, it will pass along the record to be validated for you. If that's indeed the case you could try:
def user_valid
errors.add("you must be logged in to add a thing") unless user_id?
end

How to display validation error in Rails during an update operation?

I am having a form and I am trying to save something. Whenever I hit the save button, I am running an update query in the controller. Everything works fine.
I have put a validation in place for one of the text fields which takes text lengths of minimum 5. The error appears while I create the entry. However, when I try to just update, I am not getting any error in the page (validation works though - text is not saved to DB).
How to make sure validation error appears on the page. Following is the sample code.
def save_template
#template = get_template(params[:template])
#template.update_attributes(:label => params[:label])
#some actions later
end
Please help.
The update_attributes method return true if update works and false instead.
So you just render edit template if update failed
def save_template
#template = get_template(params[:template])
unless #template.update_attributes(:label => params[:label])
render :edit
return
end
end

How do you use errors.add_to_base outside of the validates_ or validate_ model methods?

I have a Course class that has many WeightingScales and I am trying to get the WeightingScales validation to propagate through the system. The short of the problem is that the following code works except for the fact that the errors.add_to_base() call doesn't do anything (that I can see). The Course object saves just fine and the WeightingScale objects fail to save, but I don't ever see the error in the controller.
def weight_attributes=(weight_attributes)
weighting_scales.each do |scale|
scale.weight = weight_attributes.fetch(scale.id.to_s).fetch("weight")
unless scale.save
errors.add_to_base("The file is not in CSV format")
end
end
end
My question is similar to this 1: How can you add errors to a Model without being in a "validates" method?
link text
If you want the save to fail, you'll need to use a validate method. If not, you'll have to use callbacks like before_save or before_create to check that errors.invalid? is false before you allow the save to go through. Personally, i'll just use validate. Hope it helps =)
I had a similar problem, I wanted to validate a parameter that never needed to be saved to the model (just a confirmation flag).
If I did:
#user.errors.add_to_base('You need to confirm') unless params[:confirmed]
if #user.save
# yay
else
# show errors
end
it didn't work. I didn't delve into the source but from playing around in the console it looked like calling #user.save or #user.valid? cleared #user.errors before running the validations.
My workaround was to do something like:
if #user.valid? && params[:confirmed]
#user.save
# redirect to... yay!
elsif !params[:confirmed]
#user.errors.add_to_base('You need to confirm')
end
# show errors
Errors added to base after validations have been run stick around and display correctly in the view.
But this is a little different to your situation as it looks like you want to add errors in a setter not a controller action. You might want to look into before_validation or after_validation instead.

Resources