如何在不执行“before_save”的情况下进行“update_attributes”操作?

25

在我的Message模型中,我定义了一个before_save方法,如下所示:

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

当我执行以下操作时:

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

foobar被执行。

有时候,我想更新信息的字段,而不执行foobar

如何更新数据库中的created_at字段,而不执行foobar这两个方法?

5个回答

35
在Rails 3.1中,您将使用update_column
否则:
一般情况下,绕过回调函数最优雅的方式如下:
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

然后,您可以执行:

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

或者,仅针对一条记录:

my_message.update_attributes(:created_at => ..., :skip_callbacks => true)
如果您需要专门用于时间属性的话,那么如@lucapette所提及的,touch就能胜任。

看起来是一个不错的通用解决方案!一个问题:Message.batch = true 到底是做什么的? - Misha Moroshko
这只是一个标志。你可以用任何你想要的东西来替换它。 - jbescoyez
3
这在序列化的列上不起作用(即update_column),因为由于某种原因它会跳过序列化/反序列化。请注意,此处不包括其他解释或额外内容。 - bouchard
1
值得注意的是,使用update_column也意味着不会运行验证。 - In-flux
由于您正在对类设置属性,这似乎不是非常线程安全的。您有什么想法? - elsurudo

17

1
my_message.update_all(:created_at => ...) 发生了语法错误,但第二个选项运行良好! - Misha Moroshko
1
my_message.update_all 会触发 undefined method update_all 错误。 使用 Message.update_all 就可以解决问题了。 - Arivarasan L
你也许可以使用 increment_counter(如果你想要递增的是计数器),我相信它也跳过了回调。 - rogerdpack
要使用实例进行操作,您可以使用update_columnupdate_columns来避免回调调用。 - goodniceweb

6

请使用touch方法。它既优雅,又能实现你想要的功能。


看起来差不多是我需要的。在我的情况下,created_at 的新值不是当前时间。 - Misha Moroshko
@Misha 你显然是正确的。所以你可以使用http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_attribute :D - lucapette
@lucapette:怎么做?文档说update_attribute会调用回调函数。 - Misha Moroshko
@Misha,你不能这样做。考虑到你的问题,我混淆了回调和验证...所以我认为你应该使用update_all。顺便说一下,可以查看http://edgeguides.rubyonrails.org/active_record_validations_callbacks.html#skipping-callbacks,也许有些东西我忽略了。 - lucapette

2

update_columnupdate_columns 是最接近 update_attributes 的方法,它可以避免回调,而无需手动规避任何内容。


https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-touch - ronnie bermejo

1

你还可以将before_save操作设置为有条件的。

因此,添加一些字段/实例变量,并只在想要跳过它时设置它,在方法中进行检查。

例如:

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

然后在某个地方编写

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接