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

On Tue, Mar 2, 2010 at 8:54 PM, Tom M. [email protected] wrote:

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
---------
else

end

Thanks
Tom

On 3 March 2010 11:51, Tom M. [email protected] wrote:

else

end

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

On 3 March 2010 12:47, Tom M. [email protected] wrote:

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

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…

On 3 March 2010 13:16, Andy J. [email protected] wrote:

if @user.changed?
@user.save
send_mail
end

… 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
:-/

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

else

end

If your send_mail method relies on the user being saved you might need
to
alter it to be:

if @user.changed?
@user.save
send_mail
end

Cheers,

Andy

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:

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

On 3 March 2010 14:04, Andy J. [email protected] wrote:

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

else

end

Some other controller

@user = User.find(params[:user_id])
if @user.update_attributes(params[:user])
working

else

end

On 9 March 2010 08:31, Tom M. [email protected] wrote:

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)

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

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