Right now, users can edit some their attributes without having to enter their password because my validations are set up like this:
validates :password, :prese
I was having the same problem. I wasn't able to fix it with
params[:user].delete(:password) if params[:user][:password].blank?
I have only been able to get it to work by doing "update_attribute" on each item individually, e.g.
if ( @user.update_attribute(:name, params[:user][:name]) &&
@user.update_attribute(:email, params[:user][:email]) &&
@user.update_attribute(:avatar, params[:user][:avatar]) &&
@user.update_attribute(:age, params[:user][:age]) &&
@user.update_attribute(:location, params[:user][:location]) &&
@user.update_attribute(:gender, params[:user][:gender]) &&
@user.update_attribute(:blurb, params[:user][:blurb]) )
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user info"
render 'edit'
end
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!
I didn't realize the solution I gave you yesterday would lead to this problem. Sorry.
Well, taking inspiration from devise, you should simply update your controller this way:
def update
params[:user].delete(:password) if params[:user][:password].blank?
if @user.update_attributes(params[:user])
flash[:success] = "Edit Successful."
redirect_to @user
else
@title = "Edit user"
render 'edit'
end
end
I've been struggling with this and going around in circles for a while, so I thought I'd put my Rails 4 solution here.
None of the answers I've seen so far meet my use case, they all seem to involve bypassing validation in some way, but I want to be able to validate the other fields and also the password (if present). Also I'm not using devise on my project so i can't make use of anything particular to that.
Worth pointing out that it's a 2 part problem:
Step 1 - you need to remove the password and confirmation field from the strong parameters if the password is blank like so in your controller:
if myparams[:password].blank?
myparams.delete(:password)
myparams.delete(:password_confirmation)
end
Step 2 - you need to alter validation such that the password isn't validated if it's not entered. What we don't want is for it to be set to blank, hence why we removed it from our parameters earlier.
In my case this means having this as the validation in my model:
validates :password, :presence => true, :confirmation => true, length: {minimum: 7}, :if => :password
Note the :if => :password - skip checking if the password is not being set.
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:
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
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 the create
method.
This is a demo User model for illustration purposes:
class User < ApplicationRecord
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
.
.
.
end
I have no clue how to do this with devise. My two cents.
@user.username=params[:username]
if @user.update_attribute(:email,params[:email])
flash[:notice]="successful"
else
flash[:notice]="fail"
end
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.
This blog post demonstrates the principal of what you want to do.
What is not shown, but may be helpful, is to add accessors to the model:
attr_accessor :new_password, :new_password_confirmation
attr_accessible :email, :new_password, :new_password_confirmation
and to provide all of the desired validation under the condition that the user has provided a new password.
validates :new_password, :presence => true,
:length => { :within => 6..40 },
:confirmation => true,
:if => :password_changed?
Lastly, I would add a check to see if the encrypted_password has been set in order to determine if "password_changed?" in order to require a password on a new record.
def password_changed?
!@new_password.blank? or encrypted_password.blank?
end