Rails date_select帮助和验证

时间:2010-07-21 00:11:20

标签: ruby-on-rails activerecord

我的Rails应用程序中的模型支持表单中有一个日期字段:

<%= f.date_select :birthday,
  {:start_year => Time.now.year,
  :end_year => 1900,
  :use_short_month => true,
  :order => [:month, :day, :year],
  :prompt => {:month => 'Month', :day => 'Day', :year => 'Year'}},
  {:class => 'year',
  :id => 'user_birthday'}
%>

使用以下方法在模型代码中验证:

validates_presence_of :birthday, :message => 'is a required field'

不幸的是,如果用户输入的部分值(例如年份),表单仍然会提交而不会出现错误。而是将一个时髦的日期值写入数据库。如何将所有三个字段设为必填字段?

我想为此编写一个自定义验证,但我不知道如何正确访问生日元素的各个部分。我怎么能这样做?

谢谢! 萌

4 个答案:

答案 0 :(得分:2)

我认为您必须在控制器本身中创建验证。

日期部分传递给birthday(1i)birthday(2i)birthday(3i)。这里的问题是,在传递属性时会立即分配它们,因此在进行任何验证之前都会立即分配它们。

您也可以覆盖attributes=方法在那里创建自己的验证,但我不建议您这样做。

请记住,如果您进行验证,最好也可以对任何不正确的日期进行验证。 (例如2月31日,通过时将产生3月2日而不是错误)。

我认为这里的主要问题是ActiveRecord实际上在创建1之前用Date替换空值,这也意味着如果访问者只通过年份,则会创建日期那年的1月1日。我想这是一种预期的行为,只允许使用年/月/日选择之一,并仍然创建一个有用的日期。

答案 1 :(得分:1)

与此post相关,这是我找到的最佳解决方案。 但是我应该添加:day,:month,:year astr_accessible,我不明白为什么...... (因为验证?请让我知道..)

User.rb

MONTHS = ["January", 1], ["February", 2], ...
DAYS = ["01", 1], ["02", 2], ["03", 3], ...
START_YEAR = Time.now.year - 100
END_YEAR = Time.now.year
YEAR_RANGE = START_YEAR..END_YEAR

attr_accessible :day, :month, :year
attr_accessor :day, :month, :year

before_save :prepare_birthday

validate :validate_birthday

private

def prepare_birthday
  begin
    unless year.blank? # in order to avoid Year like 0000
      self.birthday = Date.new(self.year.to_i, self.month.to_i, self.day.to_i)
    end
      rescue ArgumentError
    false
  end
end

def validate_birthday
  errors.add(:birthday, "Birthday is invalid") unless prepare_birthday
end

用户注册表

<%= f.select :month, options_for_select(User::MONTHS), :include_blank => "Month" %>
<%= f.select :day, options_for_select(User::DAYS), :include_blank => "Day" %>
<%= f.select :year, options_for_select(User::YEAR_RANGE), :include_blank =>"Year" %>

答案 2 :(得分:0)

您可以覆盖validate_on_create方法,如下所示:

class MyModel < ActiveRecord::Base
  def validate_on_create
    Date.parse(birthday)
  rescue
    errors.add_to_base("Wrong date format")
  end
end

答案 3 :(得分:0)

在遵循Benoitr的建议之后,我想出了类似的使用虚拟属性的东西。在View侧,'fields_for'中有3个单独的select(年,星期一,日)。数据被提交给控制器的质量分配(控制器中没有修改,参见asciicasts #16),然后传递给模型中的getter / setter(即虚拟属性)。我正在使用Rails 3.0.3和simpleForm作为视图代码。

在视图中

<%= f.label "Design Date", :class=>"float_left" %>
<%= f.input :design_month, :label => false, :collection => 1..12 %>
<%= f.input :design_day,   :label => false, :collection => 1..31 %>
<%= f.input :design_year,  :label => false, :collection => 1900..2020 %>

在模型中:

validate :design_date_validator 

def design_year
  design_date.year
end
def design_month
  design_date.month
end
def design_day
  design_date.day
end
def design_year=(year)
  if year.to_s.blank?
    @design_date_errors = true  
  else      
    self.design_date = Date.new(year.to_i,design_date.month,design_date.day)
  end
end  
def design_month=(month)
  if month.to_s.blank?
     @design_date_errors = true  
   else      
   self.design_date = Date.new(design_date.year,month.to_i,design_date.day)
  end
end  
def design_day=(day)
  if day.to_s.blank?
    @design_date_errors = true  
  else      
   self.design_date = Date.new(design_date.year,design_date.month,day.to_i)
  end
end  
#validator
def design_date_validator
  if @design_date_errors
    errors.add(:base, "Design Date Is invalid")
  end
end  

'design_date_attr'是设置数据库中design_date值的虚拟属性。 getter传回一个类似于表单中提交的哈希值。 setter检查空白并创建一个新的日期对象并设置它并设置错误变量。自定义验证器:design_date_validator检查错误实例变量并设置errors变量。我使用':base',因为变量名不是人类可读的,使用base从错误字符串中删除该值。

重构的一些事情可能是错误检查实例变量,但它似乎至少起作用。如果有人知道更新日期对象的更好方法,我很乐意听到它。