Rails - update_attributes that arise against validation

So, I have a user model with a login, email address, password, password confirmation, name, avatar (picture), etc. There are validations on the first 5, mainly because all 5 must exist in order to create a new model.

However, this causes problems for me when updates happen.

I have an edit page where the user can edit only his name and avatar. Currently, I am not going to change their username, and I want to change the email address and password from another page.

So, the editing form looks like this:

<% form_for @user, :html => { :multipart => true } do |u| %> <p> <label>Name:</label> <%= u.text_field :name %> </p> <p> <label>Avatar:</label> <%= display_user_avatar %> <%= u.file_field :avatar%> </p> <p> <%= submit_tag %> </p> <% end %> 

If I try to execute @user.update_attributes(params[:user]) , then since only 2 parameters are name and avatar , the update does not work, because things like password, password confirmation, email, etc. are required to verify the record. d. and they simply do not exist in this form.

I can get around this by doing @user.update_attribute(:name, params[:user][:name]) , but then I worry about whether to avoid checking if this is Good Thing β„’ or not. Especially in relation to something like password updates where I need to check the new password.

Is there another way?

And if I were to do this, simply using update_attribute for :name and :avatar , how would I do it?

Will this work?

 params[:user].each do |attribute| @user.update_attribute(attribute, params[:user][attribute]) end 

Is this an acceptable way to do this ...?


- edit as a continuation -
Okie, I tried, as you suggested, and did
  def update @user = User.find_by_login(params[:id]) if @user.update_attributes!(params[:user]) redirect_to edit_user_path(@user) else flash[:notice] = @user.errors redirect_to edit_user_path(@user) end end 

So this makes the version ! , and the exception caught and displayed in the browser:

 Validation failed: Password is too short (minimum is 5 characters) 

Information in the server log:

 Processing UsersController#update (for 127.0.0.1 at 2010-07-18 11:56:59) [PUT] Parameters: {"user"=>{"name"=>"testeeeeee"}, "commit"=>"Save changes", "action"=>"update", "_method"=>"put", "authenticity_token"=>"BMEGRW/pmIJVs1zlVH2TtZX2TQW8soeCXmMx4kquzMA=", "id"=>"tester", "controller"=>"users"} 

Urm. Looking at this, I just realized that it was sending "id"=>"tester" . Now I have my routes installed, so it shows the username for users, not user_id ... Maybe that's why? It tries to find the user update with user_id == tester , but since it does not exist, is it trying to create it instead? Is this really what I am doing wrong due to the route?

Hmmm ... rake routes tells me that the route:

 edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"} PUT /users/:id(.:format) {:action=>"update", :controller=>"users"} 

And I set this route in user.rb file:

  def to_param "#{login}" end 

but it definitely displayed login instead of id all this time. But I also do it right at the beginning of the update action, @user = User.find_by_login(params[:id]) , and then update this @user .

I am very confused ..> & L;


Second update:

My user.rb stuff is as follows:

  validates_length_of :login, :within => 3..20 validates_length_of :password, :within => 5..20 validates_presence_of :login, :email, :password, :password_confirmation, :salt, :name, :on => :create validates_uniqueness_of :login, :case_sensitive => false validates_confirmation_of :password validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[az]{2,})$/i, :message => "format is invalid." attr_accessor :password, :password_confirmation 

And the hashed_password section is here:

  def password=(pass) @password = pass self.salt = User.random_string(10) if !self.salt? self.hashed_password = User.encrypt(@password, self.salt) end 

u.attributes gives me

 >> u.attributes => {"salt"=>"NHpH5glxsU", "name"=>"test er", "avatar_updated_at"=>nil, "updated_at"=>Sat Jul 17 07:04:24 UTC 2010, "avatar_file_size"=>nil, "avatar_file_name"=>nil, "hashed_password"=>"84f8675c1ed43ef7f8645a375ea9f867c9a25c83", "id"=>1, "avatar_content_type"=>nil, "login"=>"tester", "email"=>"tester@tester.com", "created_at"=>Fri May 07 10:09:37 UTC 2010} 

Urmmm ... Okay, so you said that the virtual password attribute does not actually exist ... So, how do I get around this? Bugger, here I thought I was smartly driving my own authentication code ...

How easy is it to change one of these authentication plugins? Do I need to create a new user model? Or can the plugin work with my current one?

Thanks for all the help so far, by the way !: D

+7
validation ruby-on-rails
source share
1 answer

I checked this, and partial updating of only 2 attributes with update_attributes works fine. All other attributes are left with their previous values, which means that the check should not be interrupted. A few things to try:

  • In your controller action, do you load the user through User.find ? that is, you start with a valid model.
  • Are you sure the update is not working due to validation errors? Try replacing update_attributes with update_attributes! . The latter will throw an exception if the update fails due to validation. Or check @user.errors after trying to upgrade to confirm which check failed.

Update

If User.find_by_login does not find the corresponding entry, it will return nil and will not create a new entry for you. Is it possible that the tester user in the database has a password too short? Maybe this user was created before you put the checks into your code? Do you use any plugin or callback to encrypt user passwords before saving entries? Is password actually a virtual attribute that is not saved, and the actual password is in a field of type encrypted_password ?

Try this from script/console (use the same environment in which you are testing the application: - development or production)

 > user = User.find_by_login 'tester' > user.valid? > user.attributes 

user.valid? will return true from false and tell you if the user is really working before you even try updating.

Update 2 (validation fix)

In terms of fixing your own code, you can add the following method to the User model:

 def password_validation_required? hashed_password.blank? || !@password.blank? end 

and then update all password validation rules so that they only apply if this method returns true for example.

 validates_length_of :password, :within => 5..20, :if => :password_validation_required? 

This says only the password verification rule if we do not have hashed_password (for example, for a new user) or if a new password with clear text was specified through password= . If the user already has a password and remains unchanged, skip the password check.

You are right if you want to use the plugin. Writing your own authentication code can be an interesting exercise and may be required if you have any unusual requirements. The downside is that there may be security issues that you did not think about. You don’t have to redo too much of something like restful_authentication . You just need to rename one or two fields in your User model.

+10
source share

All Articles