Updating model

hello,

i am writing a simple user login system.

when registering a user account, i have two field:

password
password_confirmation

which are validated using

validates_presence_of
validates_confirmation_of

and these are then used to generate a password hash which is stored in
my database

when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.

i want to be able to update the record, even if the user does not
specify a new password

thanks

Jon H. wrote:

maybe my post wasn’t too clear.

basically, what i am after is a way of selectively validating certain
fields of the model, i.e.

when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password

but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)

hope that’s clearer

Could you post the code that you are trying that throws errors?

Post the code where you try to update fields and save the query. Also,
post a few segments on the password stuff from the model.

-Ben L.

maybe my post wasn’t too clear.

basically, what i am after is a way of selectively validating certain
fields of the model, i.e.

when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password

but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)

hope that’s clearer

Ben L. wrote:

Jon H. wrote:

maybe my post wasn’t too clear.

basically, what i am after is a way of selectively validating certain
fields of the model, i.e.

when creating a new object and saving it to the database, i want to
validate the presence of username, password and password_confirmation,
and validate confirmation of password

but, when updating the user’s profile (name, address etc) i want to
ignore the password validation (i’ll have a seperare form for changing
password)

hope that’s clearer

Could you post the code that you are trying that throws errors?

Post the code where you try to update fields and save the query. Also,
post a few segments on the password stuff from the model.

-Ben L.

I’m running the same scheme, and here’s how I update the name of a user:

@thisUser = User.find_by_id(session[:user_id])
@thisUser[0].name = “new name!!!”
@thisUser[0].update()
@thisUser[0].save

An easier way to do this would be
User.update(session[:user_id], {:name => ‘new name!!!’})

it shouldn’t throw any errors about passwords, unless you have it rigged
so it checks the password each time you try to update the entry.

Jon H. wrote:

hi ben

i have tried your method of updating, and it’s throwing up the password
validation problems again. i probably haven’t rigged up the validation
correctly for what i am trying to do.

here is the relevant code from my user model:

class User < ActiveRecord::Base
attr_accessor :password, :password_confirmation
attr_protected :admin, :enabled, :password_salt, :password_hash
validates_uniqueness_of :username
validates_presence_of :username, :email, :password,
:password_confirmation
validates_confirmation_of :password

def before_create
salt = [Array.new(6){rand(256).chr}.join].pack(“m”).chomp
self.password_salt = salt
self.password_hash = User.hash_password(self.password, salt)
end

def after_create
@password = nil
@password_confirmation = nil
end

end

and then in my user management controller here is the update method:

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end

my understanding of update_attributes was to update and validate only
those supplied in params[:user] which afaik comes from my update form,
and therefore does not have the password and password_confirmation
fields

thanks

the first thing that jumps out at me is:

validates_presence_of :username, :email, :password,
:password_confirmation

then you have

def after_create
@password = nil
@password_confirmation = nil
end

So when you create, it validates that there’s a password. After you
have created your user with a password, it sets the password to nil.
Once you try to update a field, it does the validation methods again,
and sure enough validates_presence of :password and
:password_confirmation fail.

I think that’s what it is - lemme know.

-Ben L.

i don’t actually have password and password_confirmation fields in my
database, since i’m just storing the hash and salt obviously. so when i
load the record from the database using find() given the user id, these
are set to nil anyway.

the code i am using is based on that given in the ‘agile web development
with ruby’ book, and it sets the plaintext password to nil to prevent it
from being stored in session data (when i get round to that)

any suggestions?

hi ben

i have tried your method of updating, and it’s throwing up the password
validation problems again. i probably haven’t rigged up the validation
correctly for what i am trying to do.

here is the relevant code from my user model:

class User < ActiveRecord::Base
attr_accessor :password, :password_confirmation
attr_protected :admin, :enabled, :password_salt, :password_hash
validates_uniqueness_of :username
validates_presence_of :username, :email, :password,
:password_confirmation
validates_confirmation_of :password

def before_create
salt = [Array.new(6){rand(256).chr}.join].pack(“m”).chomp
self.password_salt = salt
self.password_hash = User.hash_password(self.password, salt)
end

def after_create
@password = nil
@password_confirmation = nil
end

end

and then in my user management controller here is the update method:

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end

my understanding of update_attributes was to update and validate only
those supplied in params[:user] which afaik comes from my update form,
and therefore does not have the password and password_confirmation
fields

thanks

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end

change to:
def update
def update
@user = User.find(params[:id])

add these three lines
@user.name = params[:user].name <— note down below
@user.update()
if @user.save

or this line
if User.update(params[:id], {:name => params[:user].name})

Note: that line is assuming your form contains this
<%= text_field ‘user’, ‘name’ %>

-Ben L.

Ben L. wrote:

def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
flash[:notice] = ‘User was successfully updated.’
redirect_to :action => ‘show’, :id => @user
else
render :action => ‘edit’
end
end

change to:
def update
def update
@user = User.find(params[:id])

add these three lines
@user.name = params[:user].name <— note down below
@user.update()
if @user.save

or this line
if User.update(params[:id], {:name => params[:user].name})

Note: that line is assuming your form contains this
<%= text_field ‘user’, ‘name’ %>

-Ben L.

the problem is that you’re using update_attributes vs. update_attribute.

update_attribues
“Updates all the attributes from the passed-in Hash and saves the
record. If the object is invalid, the saving will fail and false will be
returned.”

that params[:user] you’re passing it obviously only has this field:
<%= text_field ‘user’, ‘name’ %>

if it had:
<%= text_field ‘user’, ‘name’ %>
<%= text_field ‘user’, ‘password’ %>
<%= text_field ‘user’, ‘password_confirmation’ %>

then that syntax would have been fine, because it would have done
update_attribues and it would have passed all of the validates
requirements.

if you’re delving into agile, you should keep
http://api.rubyonrails.com/ handy. it’s important to know how to use it

  • it can really save you time. I have a copy of it downloaded through
    Web Devil (for OSX) so I can program and get any syntax help I need
    without the internet.

the problem with update_attribute is that, however, the attribute
updated is not subjest to any validation. so, for example, it would be
easy to make the mistake of updating the ‘username’ to either nil or
perhaps another username whihc already exists.

i guess what i’m trying to do if update a subset of fields, performing
validation on only those fields.

thanks

Jon H. wrote:

the problem with update_attribute is that, however, the attribute
updated is not subjest to any validation. so, for example, it would be
easy to make the mistake of updating the ‘username’ to either nil or
perhaps another username whihc already exists.

i guess what i’m trying to do if update a subset of fields, performing
validation on only those fields.

thanks

then just use update_attribute not update_attributes - that should be
exactly what you need.

On 7/18/06, Ben L. [email protected] wrote:

thanks

You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.

validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create

Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.

validate_presence_of :password, :if => Proc.new{ |model| model.something

a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false

Ben L. wrote:

then just use update_attribute not update_attributes - that should be
exactly what you need.

i have tried this, but validation checks are not performed on the
attribute being updated. for example i can update the username to
either nil or to another existing username

Daniel ----- wrote:

You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.

validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create

Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.

validate_presence_of :password, :if => Proc.new{ |model| model.something

a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false

ok i’ll give that kinda thing a try. thanks :slight_smile:

Daniel ----- wrote:

You can restrict the validation to occur only on create. That way, when
the
user is updated it won’t trip the validation trip switch.

validate_presence_of :password, :on => :create
validates_confirmation_of :password, :on => :create

Alternatively, if you need more control use the :if option to point to a
proc or a method to test if validation should occur.

validate_presence_of :password, :if => Proc.new{ |model| model.something

a_condition }
or
validate_presence_of :password, :if =>
:some_method_that_evaluates_to_true_or_false

so, is the best way of doing this to have a couple of flags in my user
model

updating_profile
updating_password

for example, which are set by the update methods in my controller, and
to use validation in my user model something like?

validates_presence_of :password, :password_confirmation :if =>
updating_password == true

sorry, you can probably tell this is my first time using ruby on rails!

is it possible to control whether the validates_* statements are
executed directly depending on the method in the controller through
which the model fields are being updated?

Alan F. wrote:

Jon H. wrote:

when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.

I think adding

:on => :create

to the end of your validates_* lines will restrict the validation to
occur only on create, not update.

Alan

yeah, that should do it for now - but i think when i come to create a
form allowing the user to update their password, then i’ll need that
password validation to kick in. i think i need to use :if =>
something

Jon H. wrote:

when i want to update the record (without changing the password and
entering new values for password and password_confirmation), using the
update_attributes method, i am getting errors thrown up by this
validation.

I think adding

:on => :create

to the end of your validates_* lines will restrict the validation to
occur only on create, not update.

Alan

On 7/19/06, Jon H. [email protected] wrote:

proc or a method to test if validation should occur.

http://lists.rubyonrails.org/mailman/listinfo/rails
One really good place to have a look at this kind of code is
techno-weenies
acts_as_authenticated. It really is a good plugin for this. Very small
and
light weight. Doesn’t get in your way.

http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated/generators/authenticated/templates/model.rb

He uses a method password_required?
A quick extract of the pertinent bits of the user model

Virtual attribute for the unencrypted password

attr_accessor :password

validates_presence_of :password, :if =>
:password_required?
validates_presence_of :password_confirmation, :if =>
:password_required?
validates_length_of :password, :within => 4…40, :if =>
:password_required?
validates_confirmation_of :password, :if =>
:password_required?

before_save :encrypt_password

Encrypts some data with the salt.

def self.encrypt(password, salt)
Digest::SHA1.hexdigest(“–#{salt}–#{password}–”)
end

Encrypts the password with the user salt

def encrypt(password)
self.class.encrypt(password, salt)
end

protected
# before filter
def encrypt_password
return if password.blank?
self.salt =
Digest::SHA1.hexdigest(“–#{Time.now.to_s}–#{login}–”)
if new_record?
self.crypted_password = encrypt(password)
end

def password_required?
  crypted_password.blank? || !password.blank?
end

Hope that gets you started.

On Mon, Jul 17, 2006 at 06:47:59PM +0200, Jon H. wrote:

hello,

i am writing a simple user login system.

did you try the LoginGenerator (or any of the alternative login systems
available) ?

see
http://wiki.rubyonrails.org/rails/pages/HowToQuicklyDoAuthenticationWithLoginGenerator
and
http://wiki.rubyonrails.org/rails/pages/LoginGenerator

Even if none of the solutions mentioned there does fit your needs, they
might provide a good starting point.

Jens

Daniel ----- wrote:

One really good place to have a look at this kind of code is
techno-weenies
acts_as_authenticated. It really is a good plugin for this. Very small
and
light weight. Doesn’t get in your way.

thanks Daniel, i’ll give those technique’s a try