我的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'
不幸的是,如果用户输入的部分值(例如年份),表单仍然会提交而不会出现错误。而是将一个时髦的日期值写入数据库。如何将所有三个字段设为必填字段?
我想为此编写一个自定义验证,但我不知道如何正确访问生日元素的各个部分。我怎么能这样做?
谢谢! 萌
答案 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,我不明白为什么...... (因为验证?请让我知道..)
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从错误字符串中删除该值。
重构的一些事情可能是错误检查实例变量,但它似乎至少起作用。如果有人知道更新日期对象的更好方法,我很乐意听到它。