【问题标题】:Using ActiveRecord, is there a way to get the old values of a record during after_update使用 ActiveRecord,有没有办法在 after_update 期间获取记录的旧值
【发布时间】:2010-10-11 01:27:40
【问题描述】:

使用简单示例进行设置:我有 1 个表 (Totals),其中包含第二个表 (Things) 中每条记录的 amount 列的总和。

thing.amount 更新时,我想简单地将旧值和新值之间的差异添加到total.sum

现在我在before_update 期间减去self.amount 并在after_update 期间添加self.amount。这使更新成功的方式过于信任。

约束:我不想简单地重新计算所有交易的总和。

问题:很简单,我想在after_update 回调期间访问原始值。你想出了什么方法来做到这一点?

更新:我同意 Luke Francl 的想法。在after_update 回调期间,您仍然可以访问self.attr_was 值,这正是我想要的。我还决定使用after_update 实现,因为我想在模型中保留这种逻辑。这样,无论我决定将来如何更新交易,我都会知道我正在正确更新交易的总和。感谢大家的实施建议。

【问题讨论】:

    标签: ruby-on-rails activerecord model callback


    【解决方案1】:

    每个人都在谈论交易。

    也就是说……

    从 Rails 2.1 开始的 ActiveRecord 跟踪对象的属性值。所以如果你有一个属性total,你就会有一个total_changed?方法和一个返回旧值的total_was方法。

    不再需要向模型添加任何内容来跟踪这一点。

    更新:这是ActiveModel::Dirty 的文档。

    【讨论】:

    • 我认为存在这样的东西。您可以链接到有关 attr_changed 的​​相关文档吗? attr_was?
    • 当然,我添加了指向答案的链接。
    【解决方案2】:

    将“_was”附加到您的属性将在保存数据之前为您提供先前的值。

    这些方法称为dirty methods 方法。

    干杯!

    【讨论】:

      【解决方案3】:

      其他一些人提到将所有这些都包含在事务中,但我认为这对你来说已经完成了; 您只需通过在 after_* 回调中引发错误异常来触发回滚。

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

      保存、保存!或销毁调用的整个回调链在事务中运行。这包括 after_* 钩子。如果一切顺利,则在链完成后执行 COMMIT。

      如果 before_* 回调取消操作,则会发出 ROLLBACK。您还可以触发 ROLLBACK 在任何回调中引发异常,包括 after_* 挂钩。但是请注意,在这种情况下,客户端需要注意它,因为普通保存会引发此类异常,而不是悄悄返回 false。

      【讨论】:

        【解决方案4】:

        要获取所有更改的字段,分别包含它们的旧值和新值:

        person = Person.create!(:name => 'Bill')
        person.name = 'Bob'
        person.save
        person.changes        # => {"name" => ["Bill", "Bob"]}
        

        【讨论】:

        • 我相信最后一行现在应该是 person.previous_changes 而不是 person.changes
        【解决方案5】:

        ActiveRecord::Dirty 是一个内置在 ActiveRecord 中的模块,用于跟踪属性更改。所以你可以使用thing.amount_was 来获取旧值。

        【讨论】:

          【解决方案6】:

          将此添加到您的模型中:

          def amount=(new_value)
              @old_amount = read_attribute(:amount)
              write_attribute(:amount,new_value)
          end
          

          然后在 after_update 代码中使用 @old_amount。

          【讨论】:

            【解决方案7】:

            如果您想在更新后获取特定字段的值,可以使用 field_before_last_save 方法。

            Example:
            
            u = User.last
            u.update(name: "abc")
            
            u.name_before_last_save will give you old value (before update value)
            

            【讨论】:

              【解决方案8】:

              Rails 5.1 开始,attribute_was 在 after 回调中的行为发生了变化。 attribute_was 将在保存完成后返回值,并在 after_saveafter_update 回调中返回当前值。

              attribute_before_last_save is invoked two ways 立即获取 after_saveafter_update 回调中字段的先前值:

              选项#1

              attribute_before_last_save('yourfield')
              

              选项#2

              *_before_last_save
              

              【讨论】:

                【解决方案9】:

                首先,您应该在事务中执行此操作,以确保您的数据一起写入。

                要回答您的问题,您可以在 before_update 中将成员变量设置为旧值,然后您可以在 after_update 中访问它,但这不是一个非常优雅的解决方案。

                【讨论】:

                • 我正在努力了解如何使用事务来实现我想要的。像这样的事务是存在于模型中还是存在于控制器中?我要删除我的“after_update”和“before_update”回调吗?最后,如何获得我需要的旧值以确定差异?
                • 没关系,我看到我必须将代码放在控制器中。我们很好。
                【解决方案10】:

                想法 1:将更新包装在数据库事务中,这样如果更新失败,您的 Totals 表不会更改:ActiveRecord Transactions docs

                想法 2:在 before_update 期间将旧值存储在 @old_total 中。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-02-14
                  • 2013-04-19
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-06-17
                  • 2021-12-03
                  • 2018-06-01
                  • 2022-11-04
                  相关资源
                  最近更新 更多