to get only the updated fields

Hi    I remember once I read about to get only updated fields from an update_attributes method. Means say I have a model user with name,age,sex etc And when the user updates the information I have to first check whether he actually updated anything(There is a chance that update button from view simply being clicked) and if only any updates happened I have to get that updated fields and send a mail stating that these fields are updated. How can I do this. Suppose in the above example if only name updated

Thanks Tom

Hi

I remember once I read about to get only updated fields from an

update_attributes method. Means say I have a model user with

name,age,sex etc And when the user updates the information I have to

first check whether he actually updated anything(There is a chance that

update button from view simply being clicked) and if only any updates

happened I have to get that updated fields and send a mail stating that

these fields are updated. How can I do this. Suppose in the above

example if only name updated

Thanks

Tom

Tom, you should be able to do the following to get the attributes that have

been changed:

model_instance.changed

Next, you’ll need to store this information prior to saving the changes

to the database. Otherwise, the above method will return an empty array.

Good luck,

-Conrad

Hi Conrad

    Thanks for your reply. But this is not working for update_attributes. What I tried is in update action

@user = User.find(params[:id]) if @user.update_attributes(params[:user])    send_mail if @user.changed? #But this is always returns false Not working

If you look at the source code for update_attributes, you'll see it does a "save" in the method, so once it's finished, the are no changed fields (because the record has already been updated).

You need a callback filter to run in your User model; and "before_save" seems sensible to me:

def before_save send_mail if self.changed? end

Hi    Thank you. I have one more thing to clarify . If I write a before_save filter as you suggested, then will mail be sent when ever there is an update or save to this User model ? That is not what I want if it behaves like that.Please excuse if I am wrong

Thanks Tom

Whatever you want will happen whenever whatever conditions you set are met.

If you send mail on any changes; then yes, mail will be sent on any changes.

Reading your original post, that's what you said you wanted...

If you look at the source code for update_attributes, you’ll see it

does a “save” in the method, so once it’s finished, the are no changed

fields (because the record has already been updated).

You need a callback filter to run in your User model; and

“before_save” seems sensible to me:

Or alternatively, as you’re looking at the source for ActiveRecord::Base you’ll see that the update_attributes method simply does this:

def update_attributes(attributes)

self.attributes = attributes

save

end

So, instead of messing about with callbacks, you could simply amend your original code to this (assigning to attributes rather than calling update_attributes):

@user = User.find(params[:id]) if @user.attributes = params[:user] send_mail if @user.changed? #But this is always returns false Not

working

@user.save

... and repeat that in every method that makes any updates to your user...

Whatever works for you, though... it's your code after all.

Be DRY :-/

While technically you’re right (and therefore it may make more sense to move it to the model - @user.update_attributes_and_notify), it seems (from reading between the OP’s lines) like there are a number of places where he’d want to update the user himself (and not send an email) and only have it send the email from a particular place/action.

I completely agree about DRYness, but I was just offering an alternative solution as we don’t know the full facts in what he’s trying to do.

:slight_smile:

Cheers,

Andy

While technically you're right

Way to get me onside :wink:

we don't know the full facts in what he's trying to do.

Agreed - the "oh, but I didn't tell you about this condition" syndrome.

:slight_smile:

Let's split the difference... create a method on the user model that checks for .changed? and sends the email, but rather than using it as an AR callback to hook *every* update, call it from those controller methods you want to, and use the default update_attributes elsewhere... how's that sound?

Something along the lines of:

# User model def update_attributes_and_notify   self.attributes = attributes   send_email if changed?   save end

# Users controller @user = User.find(params[:id]) if @user.update_attributes_and_notify(params[:user]) working

While technically you’re right

Way to get me onside :wink:

I’m a part-time developer and part-time sleazy politician :wink: “I completely agree with you 100%, however…” (notice no “but”). j/k

Let’s split the difference…

Completely agree with the solution (and it’s what I had in mind during my last message).

Cheers,

Andy

Hi    I solved it as Michael suggested And it was working .But now i notified another issue. That is why reopening the thread. The issue is, even if the validations fail, mail is sent to user (What I need is only if update happens successfully on changed attributes then only mail to be sent)

controller code is

@user = User.find(params[:id]) res = @user.update_attributes_and_notify(params[:user]) if res == true    ----do this elsif res == false    ----do this elsif res == 'nochange'   ----do this end

User model code is

  def update_attributes_and_notify(attributes,user)     self.attributes = attributes     if changed?       deliver_user_edit_details!       save     else       return 'nochange'     end   end

and if I change the line in model

   deliver_user_edit_details! if save       I get error undefined method `call' for nil:NilClass

  What would be the reason? Please help

Thanks Tom

if changed? && save deliver_user_edit_details! else return 'nochange' end