【问题标题】:Why is this callback executed?为什么要执行这个回调?
【发布时间】:2012-03-30 00:47:56
【问题描述】:

我希望在更新记录之前运行验证。我知道before_update,但我几乎从api文档中复制并粘贴了第一个coden-p。

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

我的精简模型看起来像

class User < ActiveRecord::Base
  attr_accessible :email
  validates :email, :presence => true

  before_save(:on => :update) do
    puts "******** before_save on => :update ********"
    # do something
  end
end

如果我进入控制台并创建一个 new 条目,则此回调将在 SQL 插入调用中执行。

irb(main):001:0> User.new(:email => "test@test.com").save
  (0.1ms)  begin transaction
******** before_save on => :update ********
SQL (29.1ms)  INSERT INTO "users" ("created_at", "email", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["created_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00], ["email", "test@test.com"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30 Mar 2012 00:26:33 UTC +00:00]]
   (433.1ms)  commit transaction
=> true
irb(main):002:0> 

我本来希望只在更新调用中看到这一点。任何人都可以对此有所了解吗?

[编辑]

我只是将回调更改为函数调用,结果没有变化。回调仍然在创建时执行。

class User < ActiveRecord::Base

attr_accessible :email
validates :email, :presence => true

before_save :my_before_update, :on => :update

private

def my_before_update
    puts "******** before_save on => :update ********"
    # do something
end

结束

输出是一样的。

Loading development environment (Rails 3.2.2)
irb(main):001:0> User.new(:email => "test@test.com").save
   (0.1ms)  begin transaction
******** before_save on => :update ********
  SQL (28.2ms)  INSERT INTO "users" ("created_at", "email", "first_name", "last_name",         "updated_at") VALUES (?, ?, ?, ?, ?)  [["created_at", Fri, 30 Mar 2012 02:28:45 UTC +00:00],     ["email", "test@test.com"], ["first_name", nil], ["last_name", nil], ["updated_at", Fri, 30     Mar 2012 02:28:45 UTC +00:00]]
   (131.2ms)  commit transaction
=> true

【问题讨论】:

  • 在常规运行的应用程序中的行为相同。
  • 也许我遗漏了一些东西,但我在文档中没有看到任何关于将 (:on =&gt; :update) 传递给 before_save 的内容,为什么不使用 before_update
  • 正如我之前提到的,我知道有一个before_update,但今天发生的事情是,当我完全需要它时我打开了文档,但它不起作用。 :on 在指南中用于验证 -> edgeguides.rubyonrails.org/… 回调紧随其后,文档再次提到 :on 模式。所以我拉了线......
  • 看起来 :on 用于验证,即 validates :email, :presence =&gt; true, :on =&gt; :update 只会在更新时验证电子邮件,我仍然没有看到任何说 before_save 应该采用 :on 选项。
  • updatesave 在验证和回调的上下文中并没有太大的不同......为什么要参数化 save 如果这是我唯一可以传递给 before_save(:on =&gt; 的东西?

标签: ruby-on-rails ruby-on-rails-3 activerecord


【解决方案1】:

ActiveRecord::Callbacks 不支持 :on 选项...

在 Rails 代码库中,唯一提到处理:on 选项的地方是ActiveModel::Validations 中的验证模块代码。

如果您查看ActiveRecord::Callbacks 代码,您会发现没有提及:on,ActiveRecord::Callbacks 模块也不包含任何处理该选项的ActiveModel::Validations 模块。 ActiveModel::Validations::Callbacks 有一个包含,但这只会提供before_after_ 验证方法的定义。但是,before_validationafter_validation 回调将处理 :on 选项,如其定义中的 here 所示。

【讨论】:

    【解决方案2】:

    我很确定这是 Rails API 在不同版本之间发生变化的领域之一。我记得有一种方法可以将:on 作为选项传递给before_save,就像我记得你必须定义after_initialize 方法时一样(它不能作为回调使用)。

    当前的方式更加简洁明了。

    如果您确实发现当前文档引用 before_save(:on =&gt; :update),请查看新的 docrails Github 存储库,您可以在其中对要包含的文档进行 fork、更改和提交更改(否拉取请求是必要的,或者被接受)。

    【讨论】:

    • 感谢链接。我去看看。
    【解决方案3】:

    经过一番研究,看起来你是对的,看起来你可以将:on =&gt; :update传递给before_save

    也许问题来自块符号,尝试调用这样的函数:

    before_save :run_this_before_update, :on => :update
    
    def run_this_before_update
      puts "******** before_save on => :update ********"
      # do something
    end
    

    看起来使用它的主要原因是 Rails 运行回调的顺序,请查看来自 pivotallabs http://pivotallabs.com/users/danny/blog/articles/1767-activerecord-callbacks-autosave-before-this-and-that-etc- 的这篇最优秀的文章

    【讨论】:

    • 我只是把它改成了函数调用。结果相同。回调在创建时执行。不过感谢链接。我去看看。
    • 我刚从你提到的博文中回来,他在那里所做的事情有问题......他的亲戚说,他的 current_model belongs_to :credit_card 并有一个回调 before_create :build_credit_card 这意味着,子对象current_model 在回调中创建父对象……这很可能是错误的。 foreign_key 约束也是错误的,它搞砸了关系末端被调用的顺序。
    • @mober,回复:博客文章,如果他们首先在回调中保存信用卡并将其分配给当前模型,那么belongs_to 关系将自动保存该关系。所以,这是一种有效的构建方式......
    • @Alex,他们将before_save ... :on =&gt; :create 移到belongs_to 之前这一事实可能解决了这个问题,如果他所说的关于belongs_to 被转换为before_save 的说法属实,因为那时 Rails 只是按顺序执行回调。 :on =&gt; :create 只是被忽略,因为它不是受支持的选项...
    • 是的,我打开了一个应用程序并尝试了使用 :on 选项我能想到的所有变体,但似乎没有任何效果。正如你所说的@mober,看起来那篇文章考虑了不同的情况,我想我真的没有看到需要传递 :on 选项的充分理由。
    猜你喜欢
    • 2018-01-28
    • 1970-01-01
    • 2013-06-26
    • 2016-02-11
    • 2022-01-02
    • 2010-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多