【问题标题】:How to determine if a record is just created or updated in after_save如何确定记录是刚刚在 after_save 中创建还是更新
【发布时间】:2011-02-11 23:30:55
【问题描述】:

#new_record?函数确定是否已保存记录。但在 after_save 钩子中总是错误的。有没有办法确定记录是新创建的记录还是更新的旧记录?

我希望不要使用 before_create 等其他回调在模型中设置标志或要求对数据库进行其他查询。

感谢任何建议。

编辑:需要在after_save钩子中确定它,对于我的特定用例,没有updated_atupdated_on时间戳

【问题讨论】:

  • 嗯也许在 before_save 中传递一个参数?只是大声思考

标签: ruby-on-rails activerecord activemodel


【解决方案1】:

我希望将其用于after_save 回调。

更简单的解决方案是使用 id_changed?(因为它不会在 update 上更改),如果存在时间戳列,甚至使用 created_at_changed?

更新:正如@mitsy 指出的那样,如果需要在回调之外进行此检查,请使用id_previously_changed?。见docs

【讨论】:

  • 最好用 after_update 和 after_create 来区分。回调可以共享一个公共方法,该方法接受一个参数来指示它是创建还是更新。
  • 这可能已经改变。至少在 Rails 4 中,after_save 回调在 after_create 或 after_update 回调之后运行(参见guides.rubyonrails.org/active_record_callbacks.html)。
  • 检查这些字段在after_save之外都不起作用。
  • id_changed? 将在记录保存后为假(至少在挂钩之外)。在这种情况下,您可以使用id_previously_changed?
【解决方案2】:

据我所知,这里没有轨道魔法,你必须自己做。您可以使用虚拟属性来清理它...

在您的模型类中:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end

【讨论】:

    【解决方案3】:

    还有一个选择,对于那些确实拥有updated_at时间戳的人:

    if created_at == updated_at
      # it's a newly created record
    end
    

    【讨论】:

    • 不错的主意,但在某些情况下这似乎会适得其反(不一定是防弹的)。
    • 根据迁移的运行时间,created_at 和 updated_at 记录可能会关闭。此外,您总是有可能在最初保存记录后立即更新记录,这可能会使时间不同步。这不是一个坏主意,只是感觉可以添加更多防弹实现。
    • 在最初创建记录后很长时间它们可能相等。如果记录几个月没有更新,它看起来就像是刚刚创建
    • @bschaeffer 抱歉,我的问题是“created_at 是否有可能在 after_save 回调中除首次创建时之外的任何时间等于 updated_at?”
    • @colllin:创建记录时,created_atupdated_atafter_save 回调中将相等。在所有其他情况下,它们在after_save 回调中不会相等。
    【解决方案4】:

    有一个after_create 回调仅在记录是新记录时调用,在保存后。如果这是已更改并保存的现有记录,则还有一个 after_update 回调供使用。在调用after_createafter_update 之后,在这两种情况下都会调用after_save 回调。

    如果您需要在保存新记录后执行某些操作,请使用 after_create

    更多信息在这里:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

    【讨论】:

    • 嘿,谢谢你的回答,这对我帮助很大。干杯人,+10 :)
    • 这可能不是真的。如果您在创建中有关联,则在创建关联之前调用 after_create,因此如果您需要确保创建所有内容,则需要使用 after_save
    • 赞成这个答案,因为这是完全错误的。在大多数情况下,after_create 在调用时并未将记录存储在数据库中。
    【解决方案5】:

    由于对象已经保存,您需要查看之前的更改。 ID 只能在创建后更改。

    # true if this is a new record
    @object.previous_changes[:id].any?
    

    还有一个实例变量@new_record_before_save。您可以通过执行以下操作来访问它:

    # true if this is a new record
    @object.instance_variable_get(:@new_record_before_save)
    

    两者都很丑陋,但它们可以让您知道对象是否是新创建的。希望对您有所帮助!

    【讨论】:

    • 我目前在使用 Rails 4 的 after_save 回调中处于 byebug 中,但这些都无法识别这条新记录。
    • 我想说@object.previous_changes[:id].any? 非常简单和优雅。记录更新后它对我有用(我不是从after_save 调用它)。
    • @object.id_previously_changed? 不那么丑了。
    • @MCB in after_save on Rails 4 你想看changes[:id]而不是previous_changes[:id]。然而,这在 Rails5.1 中发生了变化(参见github.com/rails/rails/pull/25337 中的讨论)
    • previous_changes.key?(:id) 以便更好地理解。
    【解决方案6】:

    Rails 5.1+ 方式:

    user = User.new
    user.save!
    user.saved_change_to_attribute?(:id) # => true
    

    【讨论】:

    • 这正是我想要的。谢谢!
    【解决方案7】:

    对于 Rails 4(在 4.2.11.1 上检查)changesprevious_changes 方法的结果是在 after_save 内创建对象时的空哈希 {}。因此,attribute_changed? 之类的 id_changed? 方法将无法按预期工作。

    但是您可以利用这些知识 - 知道在更新时至少 1 个属性必须在 changes 中 - 检查 changes 是否为空。一旦你确认它是空的,你必须在对象创建期间:

    after_save do
      if changes.empty?
        # code appropriate for object creation goes here ...
      end
    end
    

    【讨论】:

      【解决方案8】:

      对于这个用例,有一个名为 previously_new_record? 的方法。

      user = User.new
      
      user.new_record? # => true
      user.previously_new_record? # => false
      
      user.save
      
      user.new_record? # => false
      user.previously_new_record? # => true
      

      来源:https://api.rubyonrails.org/v6.1.4/classes/ActiveRecord/Persistence.html#method-i-previously_new_record-3F

      看起来通过调用saved_change_to_id? 提出的解决方法不再有效。我在 Rails 7 上。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-08
        • 2011-09-09
        • 1970-01-01
        相关资源
        最近更新 更多