首页 > 解决方案 > 当我们在 ActiveRecord 中有一个字段的新范围时,我们可以立即更改验证代码吗?

问题描述

说,如果我们有

class SomeData < ActiveRecord::Base
  validates :foo, inclusion: 33..99

现在,我们说,我们将范围缩小到66..99,但是数据库中有很多值仍然在 33 和 65 之间,我们可以将上面的代码更改为

class SomeData < ActiveRecord::Base
  validates :foo, inclusion: 66..99

立即地?如果数据只是读入系统有问题吗?(即使在早期版本的 Rails 中,例如 3.2?)

标签: ruby-on-railsactiverecord

解决方案


更改验证实际上不会以任何方式直接影响数据库中的现有数据。验证仅#valid?在模型上调用时运行。

当您调用时,这会隐式发生:

  • .create.create!
  • #save#save!
  • #update#update!

并且它会导致这些方法保释,以便首先发生回滚或不触发任何数据库查询。

我们可以立即更改验证吗?

是的。只有当您尝试更新现有记录时,验证才会真正生效。在这种情况下,以前有效的记录现在可能无效。除非将值更新到新的允许范围内,否则这将不允许更新。

处理遗留数据

如果您想让用户仍然根据新规则使用无效数据更新现有记录,则有一些技巧。

if:unless:选项_

class SomeData < ActiveRecord::Base
  validates :foo, inclusion: 33..99, if: :legacy_record?
  validates :foo, inclusion: 66..99, unless: :legacy_record?
end

有几种方法可以:legacy_record?像数据库中的布尔标志一样实现。请注意,这些不应与 ruby​​ 关键字混淆。它们只是哈希选项。

自定义验证方法:

class SomeData < ActiveRecord::Base
  validate :my_validation_method
  def my_validation_method
    rng = legacy_record? ? 33..99 : 66..99
    errors.add(:foo, "out of range") unless rng.cover?(foo)
  end
end

单表继承

class Thing < ActiveRecord::Base
  validates :foo, inclusion: 66..99
end

class LegacyThing < ActiveRecord::Base
  self.table_name = "things"
  validates :foo, inclusion: 33..99
end

在此示例中,您将添加一个things.typevarchar 列并使用things.type = "LegacyThing". 这不是真正的 STI - 它只是使用 ActiveRecord 中内置的机制。


推荐阅读