85

Possible Duplicate:
How can I avoid running ActiveRecord callbacks?

I have model like this

class Vote < ActiveRecord::Base  
    after_save :add_points_to_user

    .....
end

Is it possible to somehow force model to skip calling add_points_to_user when saved? Possibly something like ActiveRecord#delete vs ActiveRecord#destroy?

Community
  • 1
  • 1
Jakub Arnold
  • 85,596
  • 89
  • 230
  • 327
  • 1
    In my humble opinion, the question that is claimed to already have an answer, has only broken answers. So I don't think this qualifies as a duplicate. I think the answer I provide below is the officially sanctioned approach to callback obviation. – sheldonh Jun 26 '13 at 11:12

4 Answers4

174

For Rails 3, ActiveSupport::Callbacks gives you the necessary control. I was just facing the same challenge in a data integration scenario where normally-desirable-callbacks needed to be brushed aside. You can reset_callbacks en-masse, or use skip_callback to disable judiciously, like this:

Vote.skip_callback(:save, :after, :add_points_to_user)

..after which you can operate on Vote instances with :add_points_to_user inhibited

Kyle Heironimus
  • 7,741
  • 7
  • 39
  • 51
tardate
  • 16,424
  • 15
  • 50
  • 50
  • 49
    In case you didn't want to get rid of the callback permanently, but say only for one create/save then you will have to enable it back after your done skipping callbacks: `User.set_callback :save, :after, :add_points_to_user` Also do notice that if what ever you do in between the 2 calls might raise an exception you should probably wrap it around begin(-rescue)-ensure-block. – Timo Jul 01 '11 at 08:07
  • 9
    Is there similar functionality for a specific instance? – EyalB Sep 04 '12 at 12:19
  • 7
    What I'd probably suggest is, set `skip_callback` declaratively on the class with an `:if` option like `if: lambda { @skip_callbacks == true }`, and write methods to wrap cases where you need to do this: `def some_special_operation!; @skip_callbacks = true; do_something; save!; @skip_callbacks = false; end` Could use a context manager method that sets/unsets the ivar and yields to a block, but if you need this a lot there's probably something wrong ;-) **edit** Ugh, I hate SO comment newline restrictions. – ches Oct 15 '12 at 09:08
  • 5
    Watch out for how `skip_callback` and `set_callback` affect the sequence of callbacks. If I'm reading the [source code](http://api.rubyonrails.org/v3.2.16/classes/ActiveSupport/Callbacks/ClassMethods.html#method-i-set_callback) correctly, they actually delete and re-add the callback. By default, it gets added to the *end* of the chain. When setting, you can use `:prepend => :true` to force it to the beginning of the chain. Would be nice if skip/set actually did a deactivate/reactivate without affecting the sequence. My example is in [this answer](http://stackoverflow.com/a/22676587/550712). – Mark Berry Mar 27 '14 at 21:35
  • 5
    This isn't thread safe. So be cautious if you're running this on a multi-threaded server. – Arjan Aug 28 '17 at 12:00
  • 1
    For a specific instance, I used a transient attribute. I added these lines to the model: `attr_accessor :skip_my_callback` and `after_save :my_callback, unless: :skip_my_callback` And then set `my_instance.skip_my_callback = true` before saving when I wherever I wanted to skip it. – Rachel Hogue Jul 28 '22 at 22:08
47

The following applies to rails 2, rails 3 and rails 4:

http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#skipping-callbacks

It provides a list of methods that skip callbacks, explaining why it is dangerous to use them without careful consideration. Reprinted here under the provisions of the Creative Commons Attribution-Share Alike 3.0 License.

12 Skipping Callbacks

Just as with validations, it is also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential implications may lead to invalid data.

  • decrement
  • decrement_counter
  • delete
  • delete_all
  • find_by_sql
  • increment
  • increment_counter
  • toggle
  • touch
  • update_column
  • update_all
  • update_counters
sheldonh
  • 2,684
  • 24
  • 31
  • As I know, In Rails 4.2, toggle only change object, But not saved to database, you need invoke save method after toggle. if use toggle!, it saved right now, but it will trigger callback, so, it should be remove from this list. – zw963 Jun 09 '15 at 11:04
  • @zw963 But toggle! isn't on the list, so it doesn't need to be removed. Are you saying that non-bang toggle also triggers callbacks and so needs to be removed? The Rails Guide on ActiveRecord Callbacks still lists toggle among the methods that skip callbacks in 4.2.1. – sheldonh Jun 10 '15 at 12:17
  • My understanding is that some method can save data to database, and not trigger callback, it should be in the list. but, toggle is not save data. – zw963 Jun 11 '15 at 09:07
  • Ah, now I understand. No, this is not a list of methods that modify the database. It's a list of methods that may modify the state (persisted or not) of the model without triggering callbacks associated with the modified attributes. I appreciate that the original question specifically asked about skipping validations on save, but I would prefer not to edit quoted text. – sheldonh Jun 29 '15 at 18:04
28

For Rails 2, but not Rails 3 you can use these:

object.send(:create_without_callbacks)
object.send(:update_without_callbacks)
Matt
  • 13,948
  • 6
  • 44
  • 68
Stan Bright
  • 1,355
  • 1
  • 15
  • 22
28

This will skip your validations:

vote.save(:validate => false)

more info here

To skipping your callbacks and validations, you can use, update_column v(3.1), or update_all

vote = Vote.first
vote.update_column(:subject, 'CallBacks')

Aparentlly this only works with ActiveRecord 3.1

Or:

Vote.where('id = ?', YourID).update_all(:subject => 'CallBacks')

In the end you have also i finally option and this will skip everthing:

execute "UPDATE votes SET subject = 'CallBacks' WHERE id = YourID"

OK the last one it's not so pretty.

Mahmoud Khaled
  • 6,226
  • 6
  • 37
  • 42
workdreamer
  • 2,836
  • 1
  • 35
  • 37
  • 10
    this doesn't skip callbacks, just the validations – pdu Feb 21 '12 at 10:07
  • 1
    update_all does skip callbacks from http://apidock.com/rails/ActiveRecord/Relation/update_all : "It does not instantiate the involved models and it does not trigger Active Record callbacks or validations." – Kasper Grubbe Jul 25 '12 at 18:06
  • 4
    update_column works as well, except the syntax above is incorrect. It expects 2 params: vote.update_column(:subject, 'CallBacks') – yuяi Aug 14 '12 at 00:00