【问题标题】:Change attributes inside a before_save callback to pass validation?更改 before_save 回调中的属性以通过验证?
【发布时间】:2012-12-23 22:28:03
【问题描述】:

我有一个带有iv 属性的Organization 模型。如果iv 未设置,则它是随机的,但它必须是唯一的。首先想到的是下面的回调。它不起作用,因为valid? 会尝试重新验证。

class Organization < ActiveRecord::Base
  validates :iv, uniqueness: true
  before_save :set_default_iv

  def set_default_iv
    self.iv ||= random(1000)

    ### is it possible to validate like this?
    # self.iv = random(1000) until valid?
 end

【问题讨论】:

  • 你试过用before_validation代替before_save吗?
  • 在我看来before_validation 在这里没有任何作用,因为它不保证唯一性,只检查 iv 是否已经设置。
  • 如果您在验证之前执行此操作,那么您的验证器将检查它是否是唯一的。如果您要说的是您总是想要一些独特的东西并且没有重复命中的机会,那么您可能想要研究更像 UUID 的东西,它比random(1000) 发生碰撞的可能性要低得多。
  • @AlexeiDanchenkov:验证器的工作是保证唯一性,但不要更改值以使其唯一 - 如果不是,则引发错误。 before_validation 回调的工作通常是使用某种算法来尝试确保验证器得到满足。但是,调用 valid? 不能成为该算法的一部分,因为这会调用验证器本身。
  • @PinnyM:你是对的。从分配中删除缓存解决了这个问题。 before_validation 在我看来是用词不当。如果您将评论作为答案,我会接受。如果您能帮助我进行缓存分配,我将不胜感激(如果 iv 已经设置并且是唯一的,它应该保持原样而不是随机化)。

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


【解决方案1】:

按照建议使用 UUID 生成器,或使用某种算法自行检查唯一性(示例如下)。但是在 before_validation 回调中执行此操作,因此验证器将捕获任何错误。

before_validation :set_default_iv

def set_default_iv
  self.iv = random(1000) until iv.present? && !collisions_exist?
end

def collisions_exist?
  class.where({:iv => iv}).
        where(new_record? ? {} : ['id != ?', id]).exists?
end

请注意,此算法的性能会随着表的增长而降低,并且更容易发生冲突。出于这个原因,UUID 生成器肯定是比random(1000) 更推荐的方法,因为您根本不需要手动检查冲突。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-29
    • 1970-01-01
    相关资源
    最近更新 更多