I realized something quite strange when attempting to upload an image via the paperclip gem for my user model (under the avatar attribute). For some reason there User.update and #user.update_attributes behaves differently. Does anyone know why this is so?
#using #user.update_attributes(user_avatar_params)
def update_profile_pic
#user = User.find(params[:id])
#user.update_attributes(user_avatar_params)
puts #user.avatar_file_name.nil? # prints false as expected
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
#using User.update(#user.id, user_avatar_params)
def update_profile_pic
#user = User.find(params[:id])
User.update(#user.id, user_avatar_params)
puts #user.avatar_file_name.nil? # prints true although successfully saves
respond_to do |format|
format.html { redirect_to :back }
format.js
end
end
And here is my strong params in the user_controller.rb
def user_avatar_params
params.require(:user).permit(:avatar)
end
For what it's worth, as of Rails 4.0.2, #update returns false if the update failed, not simply the object which the update failed for. Of further note, #update_attributes is simply an alias of #update now.
ActiveRecord.update has a behavior that may be throwing you off:
Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
http://apidock.com/rails/ActiveRecord/Base/update/class
However the update_attributes will just return false.
Both of these use Model-level validations and so both should save or not save equally. However, the return values will be different.
As #RoaringStones pointed out, the solution is to use
user = User.update(user.id, user_avatar_params)
By the way, #update_attributes gonna be deprecated from Rails 6 (though this is not released yet)
please have a look at
https://github.com/rails/rails/pull/31998
https://github.com/rails/rails/commit/5645149d3a27054450bd1130ff5715504638a5f5
for more details.
Related
I have a controller that looks like the following:
def update
#expense_report = ExpenseReport.find(params[:id])
#expense_report.status = "Submitted"
respond_to do |format|
if #expense_report.update(expense_report_params)
...
...
else
format.html { render :edit }
...
end
end
end
I am attempting to set the status before the update, so I can reduce the number of saves performed. I'd prefer only one save to occur. However, when the form is re-rendered on a failed save, it is re-rendered with the original parameters AND the status set to Submitted, even though 'Submitted' was not one of the original params. Is there a way to render with only the original parameters?
#artur.prado is on the right track with his answer, but is missing re-assigning the user provided parameters. I've made this into a separate answer, because changing his answer would completely overhaul what he has written and might go against his intent.
You can restore the database version with restore_attributes, then re-apply the submitted changes with assign_attributes.
def update
#expense_report = ExpenseReport.find(params[:id])
#expense_report.status = "Submitted"
respond_to do |format|
if #expense_report.update(expense_report_params)
# ...
else
#expense_report.restore_attributes
#expense_report.assign_attributes(expense_report_params)
format.html { render :edit }
# ...
end
end
end
Rails 4.2 introduced exactly what you need. On the else block, just call
#expense_report.restore_attributes
before rendering the view. It will do the trick.
I am using the same commands that I previously used in successful projects but now I suddenly can't validate any updates to the object(in this case, User). Everything else works fine but any attempt to check my validations for an update results in this error-
'undefined method `valid?' for # '
It is finding successfully finding the user and if I skip the .valid? statement then it will update, just without checking any of my model validations. I recently switched from SQLite to PostgreSQL, I am not sure if that's giving me the problem. I am new to Ruby but I couldn't find anything on this specific problem.
Please let me know if I should include the entirety of my controller or any of my model but as my create works fine, I feel like all the relative code is just in this little section-
class UsersController < ApplicationController
def update
#user = User.find(params[:id])
puts "#Is this working???!! #{#user}" ///prints #Is this working???!! #<User:0x00000001f24468>
#user = User.update(user_params)
if #user.valid?
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password)
end
Your problem is here:
#user = User.update(user_params)
If you put in your check after, you would see: #Is this working???!! true, which would ironically enough inform you that it's not working.
That's because User.update(user_params) returns true or false depending on whether it is successful or not. This means your #user object is now simply either true or false, which you can't call valid on.
If you want to handle successfully updating / failing to do so, try:
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
ActiveRecord update(id, attributes)
Updates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
Then, you can do this checking if #user.update(user_params)
def update
#user = User.find(params[:id]) //Find user
if #user.update(user_params) // Update user if validations pass
redirect_to "/users/#{#user.id}"
else
flash[:errors] = #user.errors.full_messages
redirect_to "/users/#{#user.id}/edit"
end
end
Or, you can call the update method directly in your model class, but the first argument must be the user ID
User.update(params[:id], user_params)
Thank you both for your quick answers. I was replying to tell you that I already tried that and it worked but did not validate. But as two of you told me the same thing, I decided to test it out again and the validations did indeed work this time so thank you (although I definitely have a user with an email of 'asdf' from last time).
Intestering enough, I found another answer although I have no idea why it worked. I added another puts statement after the update and realized my object had been converted to an array so I came up with this other (worse) alternative answer-
def update
#user = User.find(params[:id])
puts "#Is this working???!! #{#user}"
#user = User.update(user_params)
puts "#Is this working???!! #{#user}" ///prints #Is this working???!! [#<User id: 2, name: "James Dean", etc..>]
if #user[0].valid?
redirect_to "/users/#{#user[0].id}"
else
flash[:errors] = #user[0].errors.full_messages
redirect_to "/users/#{#user[0].id}/edit"
end
end
I am working on a rails method where I need to create copies of a resource. I am using dup for that.
Right now I am using save method in saving new copies and saving it in the iteration:
(User.leads2deals.emails - [#old_inquiry.to_email]).each do |dealer|
#inquiry = #old_inquiry.dup
#inquiry.to_email = dealer
#inquiry.senttoall = true
if !#inquiry.save
all_saved = false
end
end
Depending upon the all_saved boolean flag, I am sending the right message to the user as shown:
if all_saved
#old_inquiry.senttoall = true
#old_inquiry.save
respond_to do |format|
format.html {redirect_to #parent, notice: 'All Leads were successfuly sent!'}
end
else
respond_to do |format|
format.html {redirect_to #parent, notice: 'There was some problems sending all the leads! Try sending one by one'}
end
end
My question is I want to use bulk create. Use an array, build objects and finally call create on that array of the object for efficiency.
But I want to check if create fails for any of the objects, in that case, I will change my boolean flag to false and give the user notifications that not all inquiries were saved.
I checked that create or create! does not return true/false. In this case, how can I check if all the objects were successfully created or not?
You can do:
result = Model.create(array)
Then to check if everything was created successfully,
if result.all?(&:persisted?)
# successful action
else
# failed action
end
Try using activerecord-import. It supports validations. All you need to do is to check what it returns when a validation fails and build your response based on that.
I am stumped about this. In a previous commit my forms to submit new records for a particular model worked perfectly, but I must have done something along the way that messed it up because now my controller just renders the 'new' action whenever I submit a form.
The validations are all passing, but for some reason the records are not saving.
I did not write tests during development, which I am regretting now as I am going back and writing them.
But since I am new to rails, I just want to ask about what is the best way to debug this. I've tried looking at my local server log but it does not offer anything helpful. My controller's create action is below:
def create
#product = Product.find(params[:product_id])
#review = #product.reviews.new(params[:review])
current_user.reviews << #review
if #review.save
flash[:notice] = "Successfully created review."
redirect_to current_user
else
render :action => 'new'
end
end
Apologize is this is super vague (I will delete the question if it is)
Also just to note, none of my models are saving now so it seems like an application-wide issue since they were all working at an earlier commit.
In terms of debugging things like this, I recommend two gems: better_errors and pry. Either/Or. Preferably both.
Better Errors
https://github.com/charliesome/better_errors
Just add it to your gemfile and you're done:
group :development do
gem 'better_errors'
gem 'binding_of_caller'
end
Raise an error in your controller, and you'll get dumped into an IRB session right at the point that the exception was raised. Then you can poke around and see what's happening directly.
Pry
http://pryrepl.org/
Again, just install the gem:
gem 'pry-rails'
Then restart your server (use rails s directly - passenger or foreman might screw with this) and call binding.pry from your controller:
def create
#product = Product.find(params[:product_id])
#review = #product.reviews.new(params[:review])
current_user.reviews << #review
binding.pry # <---- You will get dumped right here
if #review.save
flash[:notice] = "Successfully created review."
redirect_to current_user
else
render :action => 'new'
end
end
When you submit the form the server will stop and you should get dumped into a console right at that line. You can then poke around - inspect all the variables, call #review.save to see what happens.
Pry has a ton of features that you'll want to check out while you're in there.
And you can configure better_errors to use pry by default.
Oh, and specs are good :D
1.If you want the product_id to be set on the review, your product should be an existing record. If not you can simply do
Review.new(params[:review])
instead of
#product = Product.new(params[:product])
#review = #product.reviews.new(params[:review])
2.current_user.reviews << #review creates the review record in the database with current_user_id. you don't have to do the #review.save again.
3.Do #review.save! to test. It will raise an exception if there is problem with saving the record.
The sequence in which you're instantiating a new object and then saving it is not quite correct.
Rather than appending your uncommitted #review to current_user.reviews prior to saving it, you should only append it based conditionally on whether or not it saved correctly.
The Rails Way of accomplishing what you're attempting would be something akin to this:
def create
#product = Product.find(params[:product_id])
#review = #product.reviews.build(params[:review])
if #review.save
current_user.reviews << #review
flash[:notice] = "Successfully created review."
redirect_to current_user
else
render :action => :new
end
end
This way, if #review.save returns false, you'll know that something was invalid because the :action => :new will be rendered.
EDIT:
Since you're looking to debug without specs, you might try employing the save! method. Rather than returning false upon failure, it actually throws an exception (which is pretty handy for debugging):
#review.save!
#=> ActiveRecord::RecordInvalid: Validation failed: `reason for validation failure`
I have a Feature page that belongs to the Car page. That is working exactly how I want to, except for one thing.
After creating, updating or destroying, I want the page to be redirected to the admin_car_path(car) instead of the defaults admin_car_feature_path(car,feature) for create and update and admin_car_features_path(car).
I unsuccessfully searched for that.
ActiveAdmin.register Car do
end
ActiveAdmin.register Feature do
belongs_to :car
end
TIA
right code for updating without skipping validation
controller do
def update
super do |success,failure|
success.html { redirect_to collection_path }
end
end
end
Here is the code for update action for your case. This code goes to the features.rb - admin file:
controller do
def update
update! do |format|
format.html { redirect_to admin_cars_path }
end
end
end
This redirects to the cars index page. So you have the idea. Same for create and destroy actions.
At the current moment accepted answer leads to ignoring validation errors.
This works for me with the latest versions of ActiveAdmin and Rails:
controller do
def update
update! do |format|
format.html { redirect_to collection_path } if resource.valid?
end
end
def create
create! do |format|
format.html { redirect_to collection_path } if resource.valid?
end
end
end
Here is a solution that also works with create_another, using parent and child for model names.
This solution assumes that you show children as part of parent (e.g. via table_for) so you do not need child's index method.
In resource override controller's smart_resource_url and index methods:
controller do
def smart_resource_url
if create_another?
new_resource_url(create_another: params[:create_another])
else
parent_path(params[:parent_id])
end
end
def index
redirect_to parent_path(params[:parent_id])
end
end
Current answer is skipping validations. Some of the other answers are working but partially correct (incorrect use of super or manually validating resource).
Most updated "proper" way to redirect with AA after create and udpate:
controller do
def create
create! do |success,failure|
success.html { redirect_to collection_path, notice: "#{resource.model_name.human} was successfully created." }
end
end
def update
update! do |success,failure|
success.html { redirect_to collection_path, notice: "#{resource.model_name.human} was successfully updated." }
end
end
end
Marcelo, I'm not sure I understand your question, but wouldn't putting this into the update, create and destroy actions in your controller do the trick?
format.html { redirect_to redirect_address }
And make redirect_address whatever you need.