Misha Moroshko
Misha Moroshko

Reputation: 171369

How to "update_attributes" without executing "before_save"?

I have a before_save in my Message model defined like this:

   class Message < ActiveRecord::Base
     before_save lambda { foo(publisher); bar }
   end

When I do:

   my_message.update_attributes(:created_at => ...)

foo and bar are executed.

Sometimes, I would like to update message's fields without executing foo and bar.

How could I update, for example, the created_at field (in the database) without executing foo and bar ?

Upvotes: 25

Views: 30761

Answers (5)

Archonic
Archonic

Reputation: 5362

update_column or update_columns is the closest method to update_attributes and it avoids callbacks without having to manually circumvent anything.

Upvotes: 2

jbescoyez
jbescoyez

Reputation: 1393

In rails 3.1 you will use update_column.

Otherwise:

In general way, the most elegant way to bypass callbacks is the following:

class Message < ActiveRecord::Base
  cattr_accessor :skip_callbacks
  before_save lambda { foo(publisher); bar }, :unless => :skip_callbacks # let's say you do not want this callback to be triggered when you perform batch operations
end

Then, you can do:

Message.skip_callbacks = true # for multiple records
my_message.update_attributes(:created_at => ...)
Message.skip_callbacks = false # reset

Or, just for one record:

my_message.update_attributes(:created_at => ..., :skip_callbacks => true)

If you need it specifically for a Time attribute, then touch will do the trick as mentioned by @lucapette .

Upvotes: 35

nathanvda
nathanvda

Reputation: 50057

You could also make your before_save action conditional.

So add some field/instance variable, and set it only if you want to skip it, and check that in your method.

E.g.

before_save :do_foo_and_bar_if_allowed

attr_accessor :skip_before_save

def do_foo_and_bar_if_allowed
  unless @skip_before_save.present?
    foo(publisher)
    bar
  end
end

and then somewhere write

my_message.skip_before_save = true
my_message.update_attributes(:created_at => ...)

Upvotes: 1

lucapette
lucapette

Reputation: 20724

Use the touch method. It's elegant and does exactly what you want

Upvotes: 5

fl00r
fl00r

Reputation: 83680

update_all won't trigger callbacks

my_message.update_all(:created_at => ...)
# OR
Message.update_all({:created_at => ...}, {:id => my_message.id})

http://apidock.com/rails/ActiveRecord/Base/update_all/class

Upvotes: 17

Related Questions