我怎样才能避免运行ActiveRecordcallback?

我有一些有after_savecallback的模型。 通常这很好,但是在某些情况下,比如在创build开发数据的时候,我想保存模型而不需要运行callback。 有一个简单的方法来做到这一点? 类似于…

Person#save( :run_callbacks => false ) 

要么

 Person#save_without_callbacks 

我看了Rails文档,没有find任何东西。 但是根据我的经验,Rails文档并不总是讲述整个故事。

UPDATE

我发现了一篇博文 ,解释了如何从模型中移除callback:

 Foo.after_save.clear 

我无法find该方法的logging,但似乎工作。

这个解决scheme只有Rails 2。

我只是调查了这一点,我想我有一个解决scheme。 有两个你可以使用的ActiveRecord私有方法:

 update_without_callbacks create_without_callbacks 

你将不得不使用send来调用这些方法。 例子:

 p = Person.new(:name => 'foo') p.send(:create_without_callbacks) p = Person.find(1) p.send(:update_without_callbacks) 

这绝对是你真正想在控制台中使用或者在做一些随机testing的时候。 希望这可以帮助!

使用update_column (Rails> = v3.1)或update_columns (Rails> = 4.0)跳过callback和validation。 此外,对于这些方法, updated_at 不会更新。

 #Rails >= v3.1 only @person.update_column(:some_attribute, 'value') #Rails >= v4.0 only @person.update_columns(attributes) 

http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column

#2:跳过在创build对象时也起作用的callback

 class Person < ActiveRecord::Base attr_accessor :skip_some_callbacks before_validation :do_something after_validation :do_something_else skip_callback :validation, :before, :do_something, if: :skip_some_callbacks skip_callback :validation, :after, :do_something_else, if: :skip_some_callbacks end person = Person.new(person_params) person.skip_some_callbacks = true person.save 

更新:

@Vikrant Chaudhary的解决scheme似乎更好:

 #Rails >= v3.1 only @person.update_column(:some_attribute, 'value') #Rails >= v4.0 only @person.update_columns(attributes) 

我原来的答案是:

看到这个链接: 如何跳过ActiveRecordcallback?

在Rails3中,

假设我们有一个类的定义:

 class User < ActiveRecord::Base after_save :generate_nick_name end 

Approach1:

 User.send(:create_without_callbacks) User.send(:update_without_callbacks) 

方法2:当你想在你的rspec文件中跳过它们时,试试这个:

 User.skip_callback(:save, :after, :generate_nick_name) User.create!() 

注意:一旦完成,如果你不在rspec环境中,你应该重置callback:

 User.set_callback(:save, :after, :generate_nick_name) 

在轨道3.0.5上工作正常

导轨3:

 MyModel.send("_#{symbol}_callbacks") # list MyModel.reset_callbacks symbol # reset 

你可以在你的Person模型中尝试这样的事情:

 after_save :something_cool, :unless => :skip_callbacks def skip_callbacks ENV[RAILS_ENV] == 'development' # or something more complicated end 

编辑: after_save不是一个符号,但这是至less第一次尝试使它成为一个。

如果目标是简单地插入一个没有callback或validation的logging,并且您希望不使用额外的gem,添加条件检查,使用RAW SQL,或以任何方式使用退出的代码来实现,可以考虑使用“阴影对象“指向你现有的数据库表。 像这样:

 class ImportedPerson < ActiveRecord::Base self.table_name = 'people' end 

这适用于Rails的每个版本,是线程安全的,并且完全消除所有的validation和callback,而不需要修改现有的代码。 你可以在实际导入之前把这个类的声明扔掉,你应该很好。 只要记住使用你的新类插入对象,如:

 ImportedPerson.new( person_attributes ) 

你可以使用update_columns

 User.first.update_columns({:name => "sebastian", :age => 25}) 

更新对象的给定属性,而不调用保存,从而跳过validation和callback。

阻止所有after_savecallback的唯一方法是让第一个返回false。

也许你可以尝试像(未经testing):

 class MyModel < ActiveRecord::Base attr_accessor :skip_after_save def after_save return false if @skip_after_save ... blah blah ... end end ... m = MyModel.new # ... etc etc m.skip_after_save = true m.save 

看起来像在Rails 2.3中处理这个问题的一种方法(因为update_without_callbacks不见了,等等),将使用update_all,这是根据Rails指南的第12部分跳过callback的方法之一, 用于validation和callback 。

此外,请注意,如果您在after_callback中执行了某些操作,则会执行基于多个关联的计算(例如has_many assoc,您也在accept_nested_attributes_for中执行操作),则需要重新加载关联,以作为保存的一部分,其中一名成员被删除。

https://gist.github.com/576546

把这个猴子补丁转储到config / initializers / skip_callbacks.rb

然后

Project.skip_callbacks { @project.save }

或类似的。

都归功于作者

在一些情况下, up-voted答案可能会让人困惑。

你可以使用一个简单的检查,如果你想跳过callback,像这样:

 after_save :set_title, if: -> { !new_record? && self.name_changed? } 

一个不需要使用gem或插件就能在所有版本的Rails中工作的解决scheme就是直接发布update语句。 例如

 ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}" 

这可能(也可能不会)是一个选项,取决于你的更新有多复杂。 这适用于例如 after_savecallback中更新logging上的标志(不重新触发callback)。

 # for rails 3 if !ActiveRecord::Base.private_method_defined? :update_without_callbacks def update_without_callbacks attributes_with_values = arel_attributes_values(false, false, attribute_names) return false if attributes_with_values.empty? self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values) end end 

这些都指向without_callbacks插件,只是做你所需要的…

 class MyModel < ActiveRecord::Base before_save :do_something_before_save def after_save raise RuntimeError, "after_save called" end def do_something_before_save raise RuntimeError, "do_something_before_save called" end end o = MyModel.new MyModel.without_callbacks(:before_save, :after_save) do o.save # no exceptions raised end 

http://github.com/cjbottaro/without_callbacks适用于Rails 2.x

我写了一个在Rails 3中实现update_without_callbacks的插件:

http://github.com/dball/skip_activerecord_callbacks

我认为正确的解决scheme是重写模型以避免callback,但如果这在短期内不切实际,这个插件可能会有所帮助。

如果您使用的是Rails 2.您可以使用SQL查询更新您的列,而无需运行callback和validation。

 YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}") 

我认为它应该在任何rails版本中工作。

当我需要完全控制callback时,我创build了另一个用作开关的属性。 简单而有效:

模型:

 class MyModel < ActiveRecord::Base before_save :do_stuff, unless: :skip_do_stuff_callback attr_accessor :skip_do_stuff_callback def do_stuff puts 'do stuff callback' end end 

testing:

 m = MyModel.new() # Fire callbacks m.save # Without firing callbacks m.skip_do_stuff_callback = true m.save # Fire callbacks again m.skip_do_stuff_callback = false m.save 

你可以使用偷偷摸摸的gem: https : //rubygems.org/gems/sneaky-save 。

请注意,如果没有validation,这将无助于保存关联。 它会抛出错误“created_at不能为空”,因为它直接插入SQL查询不像模型。 为了实现这个,我们需要更新所有自动生成的db列。

你为什么要在开发中做到这一点? 这当然意味着你正在用无效的数据构build你的应用程序,因此它会performance得很奇怪,而不是你所期望的那样。

如果你想使用数据填充你的开发数据库,​​更好的方法是build立一个使用faker gem构build有效数据并将其导入数据库的rake任务,并根据需要创build尽可能多或less量的logging,但是如果你是脚跟我想这个update_without_callbacks和create_without_callbacks可以正常工作,但是当你试图按照自己的意愿弯曲钢轨时,问问自己,你有一个很好的理由,如果你正在做的是一个好主意。

一种select是使用相同的表格为这种操作提供单独的模型:

 class NoCallbacksModel < ActiveRecord::Base set_table_name 'table_name_of_model_that_has_callbacks' include CommonModelMethods # if there are : : end 

(同样的方法可以让绕过validation更容易)

斯蒂芬

另一种方法是使用validation钩子而不是callback。 例如:

 class Person < ActiveRecord::Base validate_on_create :do_something def do_something "something clever goes here" end end 

这样,你可以默认获得do_something,但是你可以很容易地使用:

 @person = Person.new @person.save(false) 

为了在Rails中创buildtesting数据,您可以使用下面的方法:

 record = Something.new(attrs) ActiveRecord::Persistence.instance_method(:create_record).bind(record).call 

https://coderwall.com/p/y3yp2q/edit

一些应该与所有版本的ActiveRecord工作,而不依赖于可能或不可能存在的选项或主动logging方法。

 module PlainModel def self.included(base) plainclass = Class.new(ActiveRecord::Base) do self.table_name = base.table_name end base.const_set(:Plain, plainclass) end end # usage class User < ActiveRecord::Base include PlainModel validates_presence_of :email end User.create(email: "") # fail due to validation User::Plain.create(email: "") # success. no validation, no callbacks user = User::Plain.find(1) user.email = "" user.save 

TLDR:在同一张表上使用“不同的主动logging模式”

我需要一个Rails 4的解决scheme,所以我想出了这个:

应用程序/模型/关注/ save_without_callbacks.rb

 module SaveWithoutCallbacks def self.included(base) base.const_set(:WithoutCallbacks, Class.new(ActiveRecord::Base) do self.table_name = base.table_name end ) end def save_without_callbacks new_record? ? create_without_callbacks : update_without_callbacks end def create_without_callbacks plain_model = self.class.const_get(:WithoutCallbacks) plain_record = plain_model.create(self.attributes) self.id = plain_record.id self.created_at = Time.zone.now self.updated_at = Time.zone.now @new_record = false true end def update_without_callbacks update_attributes = attributes.except(self.class.primary_key) update_attributes['created_at'] = Time.zone.now update_attributes['updated_at'] = Time.zone.now update_columns update_attributes end end 

在任何模型中:

 include SaveWithoutCallbacks 

然后你可以:

 record.save_without_callbacks 

要么

 Model::WithoutCallbacks.create(attributes) 

不是最干净的方式,但是您可以将callback代码封装在检查Rails环境的条件中。

 if Rails.env == 'production' ...