【问题标题】:Adding a validation to an existing database model向现有数据库模型添加验证
【发布时间】:2019-07-03 12:29:47
【问题描述】:

我是一名初级开发人员(Ruby on Rails 5),目前正在对现有模型实施新的验​​证。只有当相关属性不超过 50 个字时,验证才会通过。

  validates_length_of :reason, maximum: 50, too_long: 'Please reduce to 50 words or less',
                  tokenizer: ->(str) { str.split(/\s+/) }

很遗憾,我们的数据库中已有 54 条记录违反了此验证。因此,我正在寻找一种解决方案,以确保永远不会对这些预先存在的记录进行验证。

到目前为止,我的研究已经产生了 on: :create 选项,这似乎很有希望,但我想要一些有经验的反馈,以了解我是否会遇到问题/错误。

此验证选项是否会提供我想要的行为?

谢谢各位!

【问题讨论】:

  • 我相信两个简单的测试用例应该可以回答你的问题。如果您还没有使用测试套件 - 我非常建议您这样做。它会让事情变得更容易,尤其是在这种情况下。关于您的问题-您是否考虑过用户更新他的答案的情况?如果不可能,那么你应该没问题。如果是 - 一个肮脏的解决方案是对验证施加约束,以检查 created_at 是否大于验证之前的最后一条记录。
  • 这些旧记录在实施新验证后更新时是否允许超过50个字?或者只要不更新记录就只能有50多个字?
  • 我设想允许将旧记录更新为超过 50 个字以避免客户混淆

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


【解决方案1】:

是的,您在正确的轨道上,在处理现有数据时您有几个选择。

您应该将验证添加到on: create,因为您不希望在单独的流程中更改其他不相关字段的人员(或工作)出现错误,因为原因无效且时间过长。

但是如果你只添加on: create,这意味着有人可以用少于10个词创建一个理由,然后将更新UI上的记录更新为60个词并违反验证。还可以考虑使用if: :reason_changed? 添加相同的验证,这样可以防止更新破坏强加于创建的验证规则。

在这种情况下,您可以使用的另一个合理解决方案是对现有记录执行数据迁移,对于每条违反验证的记录,您将单词缩减为 49,在末尾添加 ... 并保存。这会丢失信息,但这意味着您可以始终 100% 地应用此验证。有时修复数据是编写更少代码的好选择。

无论您选择什么,请确保您进行了测试,以增强您对代码执行您认为应该执行的操作的信心。

【讨论】:

  • 谢谢。我想知道我是否可以按如下方式运行验证:总是在创建记录时,并且只在更新已经不超过 50 个单词的记录时。换句话说,唯一不运行验证的情况是记录已更新并且在更新之前已经有 > 50 个单词。
  • 这是一种合理的方法,请记住,这最终将成为遗留代码,新开发人员可能会对更新验证感到惊讶,因为它允许已经超过 50 个单词的记录有效。那是当您在提交消息或代码注释上留下面包屑时“~ 50 条记录在首次引入此验证时超过 50 个单词,让它们有效”或类似解释 为什么 这是完成。
【解决方案2】:

新验证仅针对创建或更新操作运行。这意味着数据库中的现有记录将保持原样。换句话说,只要它们没有更新,您就不会遇到任何问题。

这是你想要的吗?或者您是否喜欢在更新时允许旧记录> 50 字?如果您真的需要,您可以创建一个新属性,例如 legacy,您将任何现有记录设置为 true。您可以跳过对任何旧记录的验证。不过我觉得有点反了。

【讨论】:

  • 可以想象,旧的记录会被编辑。在这种情况下,如果它已经有 > 50 个字,我希望更新允许 > 50 个字(即没有验证)。在所有其他情况下(新记录、更新
  • 实现这一点的唯一方法是按照我第二段中的建议,如果您将验证更改为仅在更新时,您基本上可以通过保存和编辑到> 50个字来避免验证规则,就像达尼洛在他的回答中解释的那样。
【解决方案3】:

我会这样做:

validates_length_of :reason, maximum: 50,
                             tokenizer: ->(str) { str.split(/\s+/) },
                             too_long: 'Please reduce to 50 words or less',
                             unless: -> { reason_was && reason_was.split(/\s+/).size > 50 }

这将执行验证,除非 reason 属性以前存在并且大于 50。这意味着新记录的 reason 字数永远不会大于 50,因为它们不'没有以前的 reason 值。并且旧记录reason如果之前较大,则只能有大于 50 的字数,否则应用最大值 50。

这利用了ActiveModel::Dirty提供的方法生成方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-31
    • 1970-01-01
    • 2015-09-30
    • 1970-01-01
    相关资源
    最近更新 更多