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