【问题标题】:find_or_create_by versus before_save callbacksfind_or_create_by 与 before_save 回调
【发布时间】:2013-12-09 17:39:13
【问题描述】:

我有一个模型,它使用回调来规范化它的一个参数,并且该参数是模型唯一键的一部分。但是当使用MyModel.find_or_create_by() 时,回调不会在“查找”阶段发挥作用,只有“创建”阶段才会发挥作用,为时已晚。

这样做有没有优雅的模式?一个人为的例子:

class CreateMyModels < ActiveRecord::Migration
  def change
    create_table :my_models do |t|
      t.float :value
    end
    add_index :my_models, :value, :unique => true
  end
end

class MyModel < ActiveRecord::Base
  before_save :quantize_value
  private
    def quantize_value
      self.value = ((self.value || 0.0) * 256.0).round / 256.0
    end
end

让我们试试吧:

>> a = MyModel.find_or_create_by(:value => 0.501)
=> #<MyModel id: 1, value: 0.5>

到目前为止,一切都很好。如果我们 find_or_create_by 另一个其值接近但不等于现有记录,我们希望它找到现有记录。但相反,我们得到:

>> b = MyModel.find_or_create_by(:value => 0.5001)
PG::UniqueViolation: ERROR:  duplicate key value violates unique constraint ...

我想要 - 从概念上讲 - 让 quantize_value 方法在运行查找之前运行

当然,我已经编写了一个自定义构造函数,在运行查找之前应用 quantize_value 方法。但是,如果经验教会了我什么,那就是 Rails 的开发人员已经编写了我认为需要编写的代码,而创建我自己的构造函数就是复制他们的工作! :)

【问题讨论】:

    标签: ruby-on-rails activerecord callback ruby-on-rails-4


    【解决方案1】:

    IMO,我会将量化逻辑从保存回调的模型中移出并手动执行。您会更清楚实际值是什么,而不是设置一个值然后看到模型对其进行修改。

    如果你真的想将逻辑保留在模型中,你可以为你的属性重写 setter 方法,如下所示:

    def value=(val)
      self[:value] = quantitize_value(val)
    end
    

    【讨论】:

    • “将量化逻辑移出模型”——这是有争议的:我声称值的量化是模型的函数,不应暴露给调用者。但是我正在按照您建议的方式做一些事情,在 MyModel 中使用自定义构造函数来向调用者隐藏这些细节。
    • 如果您真的想将其保留在模型中,请尝试覆盖您的值设置方法,例如 def value=(val); @value=quantitize_value(value);结束(确实用换行符替换 ;)
    • 听起来不错。如果您编辑答案,我会接受。
    • 关闭!由于问题是关于轨道和活动记录的,我认为正确的答案是def value=(val); self[:value] = quantize_value(val) ; end。你同意吗?
    • 一个很小的小细节:它不起作用。在执行 find_or_create 的 find() 部分时,它不会调用 value= 方法。所以这仍然以与 O.P 相同的方式失败。无论如何我都会为你的努力打勾,但希望找到一个干净的解决方案。
    猜你喜欢
    • 2014-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多