如何在Rails 4中自定义验证期间将date_select哈希转换为Date对象?

时间:2014-06-12 18:00:13

标签: ruby-on-rails

我对Rails和Ruby一般都是新手。我目前正在开展个人项目,在允许用户注册之前,我必须检查用户是否至少13岁。我为此目的使用自定义验证方法,称为check_age_is_above_13。我遇到的问题是我视图中的date_select帮助将日期分为dob(1i)dob(2i)dob(3i),我无法弄清楚如何在User模型中访问这些。我的代码如下:

迁移:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email, null: false
      t.string :encrypted_password, null: false
      t.string :first_name
      t.string :last_name
      t.string :gender
      t.datetime :dob #date of birth
      t.timestamps
    end
  end
end

User模型如下所示。我跟踪了here找到的代码段,但我必须承认它有点..... out- 约会

class User < ActiveRecord::Base
  #using Devise for Auth
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :trackable,
         :confirmable

  validates :gender, inclusion: { in: %w(female male unknown) }
  validate :check_age_is_above_13

  def check_age_is_above_13
    #The following raises "undefined local variable or method `params'" exception
    dob = Date.new params[:user]['dob(1i)'].to_i, params[:user]['dob(2i)'].to_i, params[:user]['dob(3i)'].to_i
    #Trying the below code raises a "TypeError, expected numeric" exception,
    #which is understandable, as "dob" does not exist in the params list
    if ((Date.today - dob) / 365.25).to_i < 13
      errors.add(:dob, "You must be at least 13 years old to register with us")
    end
  end
end

POST注册表单时的参数如下所示:

{"utf8"=>"✓",
    "authenticity_token"=>"49w4ylxRIDyuRTDASW+vsuFL6Q3eeWcyokKrNXt9taM=",
    "user"=>{"email"=>"benny.lava@buffalax.org",
        "password"=>"[FILTERED]",
        "password_confirmation"=>"[FILTERED]",
        "first_name"=>"Benny",
        "last_name"=>"Lava",
        "gender"=>"male",
        "location_id"=>"1",
        "dob(1i)"=>"2001",
        "dob(2i)"=>"8",
        "dob(3i)"=>"27"},
    "commit"=>"Sign up"}

如何访问模型中的dob(Xi)参数,以便在对其执行自定义验证之前,先将它们转换为Date对象?我使用Devise进行用户身份验证和注册,如果它有任何区别的话。

编辑:视图如下所示:

<h2>Sign Up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>
  <div>
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true %>
  </div>
  <div>
    <%= f.label :password %><br />
    <%= f.password_field :password, autocomplete: "off" %>
  </div>
  <div>
    <%= f.label :password_confirmation %><br />
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
  </div>
  <div>
    <%= f.label :first_name %><br/>
    <%= f.text_field :first_name %>
  </div>
  <div>
    <%= f.label :last_name %><br/>
    <%= f.text_field :last_name %>
  </div>
  <div>
    <%= f.label :gender %><br/>
    <%= f.select :gender, options_for_select(%w[female male -]) %>
  </div>
  <div>
    <%= f.label :location_id %><br/>
    <%= f.select :location_id, options_for_select(Location.all.collect { |l| [l.name, l.id]}), {:include_blank => false} %>
  </div>
  <div>
    <%= f.label :dob, "Date of Birth" %><br/>
    <%= f.date_select :dob, {include_blank: false, start_year: Date.today.year - 100, end_year: Date.today.year - 13, default: {day: 31, month: 1, year: 1991}} %>
  </div>
  <div><%= f.submit "Sign up" %></div>
<% end %>

<%= render "devise/shared/links" %>

2 个答案:

答案 0 :(得分:1)

您可以从控制器访问params哈希并查看图层,但不能从模型图层访问。因此,尝试在Date模型中创建User对象有点太晚了。严格地说,您应该已经在控制器阶段填写了dob日期,但如果没有,我建议您覆盖设计控制器的create方法并设置dob变量在验证user之前,在那里用手。只是为了澄清,省略了设计的重要部分,你需要这样的东西:

class Users::RegistrationsController < Devise::RegistrationsController
  # POST /resource/sign_up
  def create
    build_resource
    resource.dob = Date.new params[:user]['dob(1i)'].to_i, params[:user]['dob(2i)'].to_i, params[:user]['dob(3i)'].to_i
    if resource.save
      set_flash_message :notice, :signed_up
      sign_in_and_redirect(resource_name, resource)
    else
      clean_up_passwords(resource)
      render_with_scope :new
    end
  end
end

完成此操作后,您应该从dob模型验证方法中删除User分配行,因为您已经设置了dob

如果您不知道从哪里开始,请查看this gist包含有关覆盖设计控制器的提示。

答案 1 :(得分:1)

严格来说,你可能不应该(并且不应该)访问rails中控制器之外的参数。

您的模型创建生命周期应如下所示:

  • 获取用户/新
  • 的请求
  • 用户控制器中的新方法
  • 提供views / users / new page

用户输入字段

  • 发布用户请求/新
  • 用户在控制器中创建方法
  • controller从params
  • 创建用户实例
  • 控制器调用保存
  • 保存用户的检查验证并返回true / false(如果已保存则为true,否则为false)
  • 控制器重定向正确

编辑看起来基本相同,只是它在控制器中使用编辑和更新方法,而模型ID通常是网址的一部分。

无论如何,如果你在控制器中正确地创建用户实例,那么在验证中你应该能够简单地调用self.dob(甚至只是dob)来访问应该是用户的内容&#39;输入的出生日期。因为您的迁移应该适当地作为某种日期。

在您的控制器中,用户创建方法可能类似于:

def create
  @user = User.new(params[:user])

  if (@user.save)
    # respond/redirect
  else
    # respond/redirect
  end
end

正如您所看到的,@ user在创建时已经从params中获得了这些值,因此您应该可以在验证中访问它们。