Right now, users can edit some their attributes without having to enter their password because my validations are set up like this:
validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :on => :create
validates :password, :confirmation => true, :length => { :within => 6..40 }, :on => :update, :unless => lambda{ |user| user.password.blank? }
However, after a user does this, their password is deleted - update_attributes is updating their password to "". Here is my update definition:
def update
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
I've also tried using a different definition that uses update_attribute instead:
def save_ff
@user = User.find(params[:id])
@user.update_attribute(:course1, params[:user][:course1] )
@user.update_attribute(:course2, params[:user][:course2] )
@user.update_attribute(:course3, params[:user][:course3] )
@user.update_attribute(:course4, params[:user][:course4] )
redirect_to @user
end
But for some reason this is doing the same thing. How can I update some user attributes without changing the password? Thanks!
The correct answer no-longer works for rails 4. I believe my answer is the cleanest and the most versatile that will work whenever you want to leave out any attributes (not just the password). This approach will be needed if you want to update the separate attributes of any model in a number of different places.
For example, if you want to do what Stack Overflow does and have the passwords updatable via a
security
page, the profile image updatable via the user show view and the bulk of a user's information updatable via a user edit view.1) Extend the
hash class
with a class method to delete blank values. We will use this method to remove blank values that are not being updated but are still present in the params hash:1a) Create a
hash.rb
file in yourlib
directory, under anext
directory:command line
1b) Inside
hash.rb
, 'create' aHash
class and create a.delete_blanks!
method:lib/ext/hash.rb
1c) Require this file (and your entire lib directory) into the rails referencing it in an initializer:
config/boot.rb
2) Inside the users#update action, implement our shiny new delete_blanks! class method to remove the attributes we're not updating from the params hash. Then, update the user instance via the
update_attributes
method, *not theupdate
method!2a) Firstly, let's use the delete_blanks! method to fix our user_params hash:
app/controllers/users_controller.rb
2b) And now let's update the instance using the
update_attributes
method, (again, not theupdate
method):app/controllers/users_controller.rb
Here's how the finished
users#update
action should look:app/controllers/users_controller.rb
3) In the
User
model, add theif: :<attribute>
option to all of your validations. This is to make sure the validation is only triggered if the attribute is present in the params hash. Ourdelete_blanks!
method will have removed the attribute from the params hash, so the validation for password, for example, won't be run. It's also worth noting thatdelete_blanks!
only removes hash entries with a value of nil, not those with empty strings. So if someone leaves out the password on the user create form (or any form with a field for the password), a presence validation will take effect because the :password entry of the hash won't be nil, it'll be an empty string:3a) Use the
if:
option on all validations:app/models/user.rb
And that's it. Now the user model can be updated over many, many different forms all over your app. Presence validations for an attribute still come into play on any form that contains a field for it, e.g. the password presence validation still would come into play in the
user#create
view.This may seem more verbose than other answers, but I believe this is the most robust way. You can update in isolation an infinite number of attributes for
User
instances, on an infinite amount of different models. Just remember when you want to do this with a new model you need to repeat the steps 2a), 2b) and 3a)I had the same problem, and the solutions above didn't work for me. I found the real culprit in my case: I had an encrypt_password callback in my User model, which was setting the password to blank each time.
before_save :encrypt_password
I fixed it by adding a condition at the end for this call back:
before_save :encrypt_password, :unless => Proc.new { |u| u.password.blank? }
I was having the same problem. I wasn't able to fix it with
I have only been able to get it to work by doing "update_attribute" on each item individually, e.g.
which is clearly a total hack but its the only way I can figure it out without messing with the validations and deleting the password!
2017 answer:
In Rails 5 as also indicated by Michael Hartl's tutorial, it's enought that you have something along these lines in your model:
allow_nil: true is the key here which allows a user to edit his/her info without also requiring a password change too.
At this point one might think that this will also allow empty user signups; However this is prevented by using the
has_secure_password
which automatically validates password presence but only thecreate
method.This is a demo User model for illustration purposes:
I have no clue how to do this with devise. My two cents.
above code can update username and email field. because update_attribute can update dirty fields. but it is a pity, update_attribute would skip validation.