【问题标题】:Rails date of birth confirmation validationRails 出生日期确认验证
【发布时间】:2020-06-18 07:32:57
【问题描述】:

我想验证我的用户是否注册。我已经将他们的出生日期存储在我的数据库中,因为他们是从 API 客户端发送过来的。我希望用户确认他们的出生日期,所以我设置了 Rails 确认验证。我的参数中有date_of_birth_confirmation,这就是我在表单中使用的。

          <%= f.label :date_of_birth_confirmation, "DATE OF BIRTH (MM/DD/YYYY)" %>
          <%= f.date_select :date_of_birth_confirmation, ...

我可以提交并获取我的参数,但确认验证不起作用。我的参数是通过

  "user" => { "date_of_birth_confirmation(2i)"=>"10", "date_of_birth_confirmation(3i)"=>"5", "date_of_birth_confirmation(1i)"=>"1927" }

我相信这是日期选择的预期。据我了解,如果只是用户的 date_of_birth 而不是他们的 dob_confirmation,Rails 会在将其保存在数据库中时将其转换为日期对象。

但是当与实际的 date_of_birth 值进行比较时,即使它们匹配,它也总是会失败。我认为这是因为confirmation 验证的工作原理。由于date_of_birth_confirmation 只是一个字符串,这意味着我需要将 dob_confirmation 转换为日期,以便可以比较它们,所以这最终成为我的解决方案

  def verification_params
    params[:user][:date_of_birth_confirmation] = format_date_of_birth_confirmation
    params.require(:user).permit(
      :date_of_birth_confirmation
    )
  end

  # Format date_of_birth_confirmation to be a date so the comparison can be made with date_of_birth
  def format_date_of_birth_confirmation
    year = params[:user].delete("date_of_birth_confirmation(1i)")
    month = params[:user].delete("date_of_birth_confirmation(2i)")
    day = params[:user].delete("date_of_birth_confirmation(3i)")

    return nil unless year.present? && month.present? && day.present?

    "#{year}-#{month}-#{day}".to_date
  end

当我测试它时,这似乎有效。哪个好!

但我想知道是否有更好的解决方案?我不完全确定是否有更“有条理”的方式来解决这个问题。

【问题讨论】:

    标签: ruby-on-rails validation


    【解决方案1】:

    简短的回答是否定的,没有比您正在做的更直接的“rails-y”方式了,因为 date_select 总是将原始选定值作为独立字符串返回。

    【讨论】:

      【解决方案2】:

      使用validations

      首先,验证date_of_birth 是一个日期,而不是空白,并且是一个合理的日期。我用validates_timeliness

      class User < ApplicationRecord
        # Validate the actual date of birth.
        validates :date_of_birth,
          confirmation: true,
          presence: true,
          timeliness: {
            type => :date,
            # They're not from the future.
            on_or_before: lambda { Date.current },
            # They're not 120.
            after: '1900-01-01'
          }
      end
      

      然后制作一个表单模型来验证和处理表单输入。通过包含ActiveModel::Model,我们得到了一个类,它可以做许多与记录相同的事情,但没有表。

      class DateForm
        include ActiveModel::Model
      
        attr_accessor :year, :month, :day
      
        validates :year, :month, :day
          # They must be positive integers
          presence: true,
          numericality: { only_integer: true, greater_than_or_equal_to: 1 }
      
        def to_date
          Date.new(year, month, day)
        end
      end
      

      然后您可以在控制器中使用 DateForm 来验证表单并将日期传递给用户。

      def new
        @birth_date = DateForm.new
        @birth_date_confirmation = DateForm.new
        @user = User.new
      end
      
      def create
        @user = User.new(params[:user])
        @birth_date = DateForm.new(
          year = params[:birth_date]["date_of_birth(1i)"]
          month = params[:birth_date]["date_of_birth(2i)"]
          day = params[:birth_date]["date_of_birth(3i)"]
        )
        @birth_date_confirmation = DateForm.new(
          year = params[:birth_date_confirmation]["date_of_birth_confirmation(1i)"]
          month = params[:birth_date_confirmation]["date_of_birth_confirmation(2i)"]
          day = params[:birth_date_confirmation]["date_of_birth_confirmation(3i)"]
        )
        render 'new' if !@birth_date.valid? || !@birth_date_confirmation.valid?
      
        @user.date_of_birth = @birth_date.to_date
        @user.date_of_birth_confirmation = @birth_date_confirmation.to_date
      
        if @user.save
          redirect_to @user
        else
          render 'new'
        end
      end
      

      并在您的视图中使用@birth_date 和@birth_date_confirmation 作为它们自己的表单。

      form_for(@birth_date, as: :birth_date do |f|
        ...
      end
      
      form_for(@birth_date_confirmation, as: :birth_date_confirmation) do |f|
        ...
      end
      

      这也为您提供了一个模型,您可以轻松地进行单元测试,从而使您的控制器测试更简单。

      【讨论】:

      • 该答案似乎无法验证我正在尝试做的确认。我看不到在您包含的示例中,确认验证将在哪里运行,或者它将如何针对 DateForm 模型运行。我想我需要另一个实例方法或验证才能运行。在那一点上,我觉得要写出很多代码,但在可读性方面几乎没有什么好处。出生日期不是我用来确认用户注册的唯一参数。我觉得我所拥有的可能是这种情况的最佳解决方案。
      • @Gabriel OIC。使用两个 DateForms,一个用于 DOB,一个用于确认。将confirmation: true 添加到用户的date_of_birth 验证中。
      • @Gabriel 我可能有点过头了。
      【解决方案3】:

      我最终更改了控制器逻辑,改为将格式化 date_of_birth_confirmation 的逻辑放入模型中,这可能更适合它。

         # User.rb
         before_validation :format_date_of_birth_confirmation
      
         def format_date_of_birth_confirmation
          return if date_of_birth_confirmation.blank?
          return if date_of_birth_confirmation.is_a?(Date)
      
          year = date_of_birth_confirmation[1]
          month = date_of_birth_confirmation[2]
          day = date_of_birth_confirmation[3]
      
          return unless year && month && day
      
          assign_attributes(date_of_birth_confirmation: "#{year}-#{month}-#{day}".to_date)
        end
      

      【讨论】:

        猜你喜欢
        • 2022-01-24
        • 1970-01-01
        • 2017-04-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多