Topic: Confusing problem of database record update. Help!!!

I need to update several parameters of a user account table.
At this moment, I am writing the code to update the user name and user role attributes.

Here is the update method:

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(params[:user])
      flash[:notice] = 'Account was successfully updated.'
      redirect_to :action => 'show_user', :id => @user
    else
      render :action => 'edit_user'
    end
  end

Here is the partial:

<%= error_messages_for 'user' %>
<!--[form:user]-->
<p><label for="user_name">Name</label><br/>
<%= text_field 'user', 'name'  %></p>
<p><label for="user_role">Role</label><br/>
<%= text_field 'user', 'role'  %></p>
<!--[eoform:user]-->

Who can tell me what is the "params[:user]" in this line and how to use it?
    if @user.update_attributes(params[:user])
because I need only to update several parameters and the user table contains hashed passwords, So I can not use this code to update the whole record.

Thanks

Last edited by william (2007-01-31 18:22:16)

Re: Confusing problem of database record update. Help!!!

See my tutorial on understanding what is going on behind the scenes with the form. If you still have questions, please post them here.

Railscasts - Free Ruby on Rails Screencasts

Re: Confusing problem of database record update. Help!!!

Yes, thank you very much for your instruction. your tutorial is very clear and professional. I understand the meaning of :params[:user]

However, my problem is I did meet a validation error because the table contains hashed password column and salt.

With which helper, I can only modify several columns of a table but rewrite all the fields including the hashed password? update_attributes? How can I only refer several values of the parameters hash in the controller? For example, in your tutorial, if I only want to modify name and leave the email attribute unchanged. How can I use the parameter hash in the controller please? furthermore, if in case in the example table, besides the name and email, you have columns of hashed password and salt, you need only to change the name and email, you do not want to change the hashed password and salt. How will you do? At the flip side, now, if you want to leave the name and email unchanged and you would like to offer a function of change users' password only, what is your approach please? I am writing the admin module of my first RoR website, it is very important to me. Thank you very much.

Re: Confusing problem of database record update. Help!!!

I don't have an answer for you, since I'm still at the beginning stages, but looks like you need to delete the password field in the user has before passing it to save.  I'm curious to see the real answer since I'll need to deal with this eventually as well.

Re: Confusing problem of database record update. Help!!!

When calling update_attributes as you are doing in your original post:

@user.update_attributes(params[:user])

This should only update the attributes which are submitted (which you have fields for). So in this case it should only update the name and role parameters. It should leave the password_salt/hash attributes untouched if you don't have edit fields for them.

However, do you have a text field for setting the password and you want to only have it update the password if it is not blank? In that case you can remove the password attribute from the hash if it is blank. For example:

  def update
    @user = User.find(params[:id])
    params[:user].delete(:password) if params[:user][:password].blank?
    if @user.update_attributes(params[:user])
      flash[:notice] = 'Account was successfully updated.'
      redirect_to :action => 'show_user', :id => @user
    else
      render :action => 'edit_user'
    end
  end

Then when you call update_attributes, the password attribute will not be updated.

There might be a better way to do it, but is this even what you are looking for?

Railscasts - Free Ruby on Rails Screencasts

Re: Confusing problem of database record update. Help!!!

yes, that is right what I am looking for.
I am trying this out now. Thanks, I am also looking at your post of http://railsforum.com/viewtopic.php?id=509 too.

Thank you very much!

Re: Confusing problem of database record update. Help!!!

Something is still there. Following is the flashed error message:

3 errors prohibited this user from being saved
There were problems with the following fields:

Password confirmation can't be blank
Password should be at least 5 characters long
Password can't be blank

Can I post an image here please and how?

==================================================
Following are all the related codes.
in admin_controoler:

  def update_user
    @user = User.find(params[:id])
    params[:user].delete(:hashed_password) if params[:user][:hashed_password].blank?
    if @user.update_attributes(params[:user])
      flash[:notice] = 'Account was successfully updated.'
      redirect_to :action => 'user_management'
    else
      render :action => 'edit_user'
    end
  end
============================
in _accountform.rhtml

<%= error_messages_for 'user' %>

<!--[form:user]-->
<p><label for="user_name">Name</label><br/>
<%= text_field 'user', 'name'  %></p>

<p><label for="user_role">Role</label><br/>
<%= text_field 'user', 'role'  %></p>

<!--[eoform:user]-->

=============================
in edit_user.rhtml

<h1>Editing account</h1>

<%= start_form_tag :action => 'update_user', :id => @user.id %>
  <%= render :partial => 'accouuntform' %>
  <%= submit_tag 'Edit' %>
<%= end_form_tag %>

<%= link_to 'Back to users list', :action => 'user_management' %>

==============================
user.rb model

require 'digest/sha1'

class User < ActiveRecord::Base
    validates_presence_of :name,
                          :password,
                          :password_confirmation,
                          :role
    validates_uniqueness_of :name
    validates_length_of :password,
                        :minimum => 5,
                        :message => "should be at least 5 characters long"
    attr_accessor :password_confirmation
    validates_confirmation_of :password
   
    def password
      @password
    end

    def password=(pwd)
      @password = pwd
      create_new_salt
      self.hashed_password = User.encrypted_password(self.password, self.salt)
    end
   
    def self.authenticate(name, password)
      user = self.find_by_name(name)
      if user
        expected_password = encrypted_password(password, user.salt)
        if user.hashed_password != expected_password
          user = nil
        end
      end
      user
    end
   
    private
   
    def self.encrypted_password(password, salt)
      string_to_hash = password + "wibble" + salt # 'wibble' makes it harder to guess
      Digest::SHA1.hexdigest(string_to_hash)
    end
   
    def create_new_salt
      self.salt = self.object_id.to_s + rand.to_s
    end
   
end
=================================================
all the codes are here.
I am so confusing.

Last edited by william (2007-02-02 14:26:11)

Re: Confusing problem of database record update. Help!!!

the users table has only 5 columns
id, name, hashed_password, salt, role

Re: Confusing problem of database record update. Help!!!

that should be the problem of "validates_presence_of"
when creating a new user, it is should be sure that each user should have a password.
However, when a user or administrator maneges the user account. It does not make sense to include the password field each time anyway. How to modify the validation code please?

Re: Confusing problem of database record update. Help!!!

I have solved this problem. thanks anyway.
change the validation code like this:

    validates_presence_of :name,
                          :password,
                          :password_confirmation,
                          :role,
                          :on => :create
    validates_uniqueness_of :name
    validates_length_of :password,
                        :minimum => 5,
                        :on => :create,
                        :message => "should be at least 5 characters long"
    attr_accessor :password_confirmation
    validates_confirmation_of :password

It works. I am not sure if it is a right method.
Could you please check that for me please?

Last edited by william (2007-02-02 14:50:11)

Re: Confusing problem of database record update. Help!!!

however, I can see another problem.

In this case, the password and conformation field can be blank when modifying the account information. That means a user can change his/her password to "nothing" or "blank" which is not permitted by the administration. How can I deal with this now?

Re: Confusing problem of database record update. Help!!!

If you use the code I gave above:

  def update
    @user = User.find(params[:id])
    params[:user].delete(:password) if params[:user][:password].blank?
    if @user.update_attributes(params[:user])
      flash[:notice] = 'Account was successfully updated.'
      redirect_to :action => 'show_user', :id => @user
    else
      render :action => 'edit_user'
    end
  end

Any blank password should be removed so it is not set/updated if a user attempts to set the password as blank. Does this solve your problem?

Railscasts - Free Ruby on Rails Screencasts

Re: Confusing problem of database record update. Help!!!

this problem was created by the data validation process.
When I spicify :on => :create for a data validation command, everything is ok. However, the validation should be done every time the information is modified too. So, is there a way that the data will only be validated when itself is changed?

Last edited by william (2007-02-05 10:13:39)

Re: Confusing problem of database record update. Help!!!

can I use this?

validates_length_of :password,
                    :minimum =>5,
                    :on => :create :update

looks not correct.

Last edited by william (2007-02-05 10:42:14)

Re: Confusing problem of database record update. Help!!!

I used a validation for thr length of password.
    validates_length_of :password,
                        :minimum => 5,
                        :message => "should be at least 5 characters long"
My table has 5 columns
id, name, hashed_password, salt, role.

  def update_user
    @user = User.find(params[:id])
    params[:user].delete(password) if    params[:user][password].blank?
    if @user.update_attributes(params[:user])
      flash[:notice] = 'Account was successfully updated.'
      redirect_to :action => 'user_management'
    else
      render :action => 'edit_user'
    end
  end

With this validation I can not change other attributes if I do not change password. The validation process takes effect even when I am not updating the password.  It is strange!!!

Please help me, it used my 3 days .........

Last edited by william (2007-02-05 12:15:34)

Re: Confusing problem of database record update. Help!!!

Perhaps this is what you want:

class User < ActiveRecord::Base
  validates_length_of :password,
                        :minimum => 5,
                        :if => :password_set?,
                        :message => "should be at least 5 characters long"
 
  def password_set?
    !@password.blank?
  end
end

This should only call this validation when the password is filled (being updated/created).

Railscasts - Free Ruby on Rails Screencasts

Re: Confusing problem of database record update. Help!!!

ryanb wrote:

Perhaps this is what you want:

class User < ActiveRecord::Base
  validates_length_of :password,
                        :minimum => 5,
                        :if => :password_set?,
                        :message => "should be at least 5 characters long"
 
  def password_set?
    !@password.blank?
  end
end

This should only call this validation when the password is filled (being updated/created).

OMG, finally it is resolved. YOU did it!
Thanks a lot Ryanb!!!