防止实例上的 ActiveRecord save()

Posted

技术标签:

【中文标题】防止实例上的 ActiveRecord save()【英文标题】:Preventing ActiveRecord save() on an instance 【发布时间】:2011-02-25 14:12:59 【问题描述】:

我有一个 ActiveRecord 模型对象Foo;它代表一个标准的数据库行。

我希望能够显示此对象实例的修改版本。我想重用这个类本身,因为它已经拥有我需要的所有钩子和方面。 (例如:我已经有一个显示适当属性的视图)。基本上我想克隆模型实例,修改它的一些属性,并将其反馈给调用者(视图、测试等)。

我确实希望这些属性修改返回到数据库中。但是,我确实想在克隆版本中包含 id 属性,因为它可以更轻松地处理路由助手。因此,我计划调用ActiveRecord::Base.clone(),手动设置克隆实例的ID,然后对新实例进行适当的属性更改。这让我很担心;修改后的实例上有一个save(),我的原始数据将被破坏。

所以,我希望锁定新实例,以免它伤害到其他任何东西。我已经计划打电话给freeze()(虽然documentation isn't terribly clear,但这会阻止对对象的进一步修改)。但是,我没有看到任何明显的方法来阻止 save()。

实现这一目标的最佳方法是什么?

【问题讨论】:

如果你需要用标准的 ActiveRecord 对象来解决这个问题,可能有更好的方法来解决这个问题。 我当然愿意接受替代品 【参考方案1】:

您可以使用ActiveRecord::Base#readonly!

model = MyModel.find 1
model.readonly!
model.save!

它将引发 ActiveRecord::ReadOnlyRecord

【讨论】:

【参考方案2】:

freeze() 似乎正在实现我想要的,尽管方式很丑。

x = Factory.create(:my_model)
x.save!    # true
x.freeze
x.save!
TypeError: can't modify frozen hash

我猜测 save() 正在尝试更新创建/修改的属性,但由于属性哈希被冻结而失败。

因此,冻结会阻止保存...但我希望有更可靠的方法和更具体的错误消息。

【讨论】:

只是为了澄清,冻结将阻止实例被修改,而只读!将允许修改但阻止将修改后的实例保存到数据库中。 感谢@DavidAldridge 的更新;我已更改接受的答案。【参考方案3】:

可能有一种更惯用的方法来做到这一点,但一种方法是设置一个虚拟属性并在before_save 回调中检查它。克隆对象时,设置虚拟属性 - 可能类似于 is_clonetrue。然后为您的模型类定义一个before_save 回调,如果设置了该属性,则该回调会阻止保存。

【讨论】:

以上是关于防止实例上的 ActiveRecord save()的主要内容,如果未能解决你的问题,请参考以下文章

修改 ActiveRecord 属性仅用于保存

Rails 3:取消在 after_save 上的插入

Yii2 ActiveRecord save失败

Rails ActiveRecord 在 after_save 回调中使用最近保存的记录 ID [关闭]

为啥在 Spring Data JPA Repository 上的 save() 之后使用返回的实例?

Rails ActiveRecord::Base before_save 覆盖 write_ 访问器以保存电话号码