show error messages from two models when updating Rails - ruby-on-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

Related

Rails Controller Object.save

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.

Using an if statement with Rails ActiveRecord find method

I want to check that a user is included in a group. In an attempt to do this, I have the following statement:
user_id = current_user.id
unless (group.user.find(user_id))
redirect_to different_path
end
Looking at the docs, "A failure to find the requested object raises a ResourceNotFound
exception if the find was called with an id." How do I write the unless statement so that it operates properly?
If you defined a relation between group and users, you can call current_user.group.present? This will return false if there is no related object.
You can handle the redirect as part of Exception handling
redirect_to path if group.user.find(user_id)
rescue ResourceNotFound
redirect_to differenct_path
end
alternatively and probably a better way would be to build your logic around
user.groups.include?(group)

In rails, how can I find out what caused a .save() to fail, other than validation errors?

I have an ActiveRecord model which is returning true from valid? (and .errors is empty), but is returning false from save(). If the model instance is valid, how can I find out what's causing the save to fail?
If #user.save (for example) returns false, then just run this to get all the errors:
#user.errors.full_messages
Try using the bang version save! (with an exclamation mark at the end) and inspecting the resulting error.
Check all your callbacks.
I had a problem like this where I had and "after_validate" method that was failing after I had made a bunch of changes to the model. The model was valid but the "after_validate" was returning false, so if I used model.valid it said true, but then if I saved it gave me validation errors (passed through from the after_validate callback). It was weird.
Look at the application trace and you should be able to see what line of code is raising the exception.
Yea, I fixed this issue by making sure I return true in all my before_* callbacks then it starts working :)
Make sure you aren't trying to save a deleted record.
I had the same issue. But unlike the selected answer - my issue wasn't callbacks related.
In my case I had tried to save to a deleted record (deleted from DB).
#user = User.new
#user.save! # user saved to DB
#user.persisted? # true
#user.destroy # user deleted from DB
#user.persisted? # false, user still has its id
#user.valid? # return true
#user.errors # empty
#user.save # return false
#user.save! # raise ActiveRecord::RecordNotSaved
The problem I had was that I had forgotten to add the validation to the model.
class ContactGroup < ActiveRecord::Base
validates_presence_of :name
end

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 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