在现有记录上添加状态验证

时间:2017-01-04 23:02:00

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

我们的用户记录具有名为first_name的属性。其中许多记录没有填写first_name属性,因此它等于零。我们想在此属性上引入状态验证。然而,我们遇到了一个巨大的问题。如果用户在任何请求期间更新其记录,则该请求将失败。这导致了一个相当严重的错误,我们不知道如何处理。

一种解决方案是仅在用户创建记录时调用验证。这很有用但我们希望在个人资料页面上强制执行此验证,并且他们正在尝试更新其个人资料。

有没有更好的方法来处理这个问题,我们可以在更新页面上强制执行名字要求但仍然允许用户更新他们的记录?

1 个答案:

答案 0 :(得分:4)

对不满足新要求的现有数据引入验证可能会有问题。您之后的这个概念基本上是写入时的迁移:您已经引入了随着时间的推移发生的数据迁移,因为如果没有单独的用户输入,迁移就不会发生。这是一种在零停机环境中迁移非常大的数据集或强制用户重置密码的技术。

从根本上说,您需要定义必须进行验证的条件,并找到测试该条件的记录(创建或更新)的方法。您的条件应该选择所有新记录,以及在可以迁移的上下文中更新的记录。

一旦您定义了条件,就可以修改验证:

validates :first_name, presence: true, if: -> { condition_for_migration }

理想情况下,条件应该是表格中已存在的某些字段或字段组合,这些字段或字段组合可以正确地将记录标识为可以迁移,但这并非总是可行。

如果不这样做,您可以专门为此目的引入一个字段。您可以将其称为version_number,将所有现有记录设置为1,然后为所有新记录2设置默认值。您的迁移可能如下所示:

# All existing records will have their `version_number` set to the default of 1
add_column :users, :version_number: :integer, null: false, default: 1

# Change the default to 2 for any records created after this point
change_column_default :users, :version_number, 2

然后,您可以使用version_number来判断是否应该进行验证:

validates :first_name, presence: true, if: -> { version_number >= 2 }

关键是要确保在您的个人资料表单的上下文中,您还要更新version_number以启用first_name的验证:

 # app/viws/users/edit.html.haml
 = form_for @user do |f|
   = f.hidden_field :version, value: 2
   = f.input :first_name

如果没有用于此目的的真实数据库字段,您可以向模型添加临时数据库字段,该模型仅在特定模型实例的生命周期内维护上下文:

  1. 为模型添加访问者,即update_from_profile_page
  2. 在您要求验证的上下文中包含该字段
  3. 在创建任何新记录期间验证first_name
  4. first_name为真的任何更新
  5. 期间验证update_from_profile_page

    例如:

    应用程序/模型/ user.rb

    class User < ActiveRecord::Base
      attr_accessor :update_from_profile_page
    
    
      validates :first_name, presence: true, on: :create
      validates :first_name, presence: true, on: :update, if: -> { update_from_profile_page }
    end
    

    app / views / user / edit.html.haml(您的个人资料页面)

    = form_for @user do |f|
      = f.input :first_name
    

    应用程序/控制器/ users_controller.rb

    def update
      @user = User.find(params[:id])
      @user = update_from_profile_page = true
      @user.update(params.require(:user).permit(:first_name)
    end
    

    这比为条件验证找到具体的基于业务逻辑的原因更不可取,因为它涉及向模型引入虚拟字段,该字段在表单提交的单个特定情况之外没有任何功能值。