简体   繁体   English

Rails date_select帮助和验证

[英]Rails date_select helper and validation

I have a date field in a model backed form in my Rails App: 我在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'}
%>

It is being validated in the model code using: 它在模型代码中使用以下方式进行验证:

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

Unfortunately, if the user enters a partial value such as just the year, the form still submits without an error. 不幸的是,如果用户输入的部分值(例如年份),表单仍然会提交而没有错误。 Instead a funky date value gets written to the db. 而是将一个时髦的日期值写入数据库。 How do I make all three fields be mandatory? 如何使所有三个字段成为强制字段?

I'd like to write a custom validation for this, but I don't know how to properly access the indvidual pieces of the birthday element. 我想为此编写一个自定义验证,但我不知道如何正确访问生日元素的各个部分。 How can I do this? 我怎样才能做到这一点?

Thanks! 谢谢! Moe

I think you would have to create the validation in the controller itself. 我想你必须在控制器本身创建验证。

The date parts are being passed to birthday(1i) , birthday(2i) and birthday(3i) . 日期部分被传递到birthday(1i)birthday(2i)birthday(3i) The problem here is that they are assigned immediately when passing the attributes and thus before any validations occur. 这里的问题是它们在传递属性时立即分配,因此在任何验证发生之前。

You could also overwrite the attributes= method to create your own validation there, but I would not suggest you to do that. 您也可以覆盖attributes=方法在那里创建自己的验证,但我不建议您这样做。

Keep in mind that if you do validations, it might be good to validate against any incorrect date as well. 请记住,如果您进行验证,也可以对任何不正确的日期进行验证。 (for instance 31st of February, which when passed will yield 2nd of March and not an error). (例如2月31日,通过时将产生3月2日而不是错误)。

I think the main issue here is that ActiveRecord is actually replacing the empty values with 1 before creating the Date , which also means that if the visitor pass only the year, the date will be created on the 1st of January that year. 我认为这里的主要问题是ActiveRecord实际上在创建Date之前将空值替换为1 ,这也意味着如果访问者仅通过年份,则日期将在该年的1月1日创建。 I guess that is an expected behaviour to allow use of only one of year/month/day select and still create a useful date. 我想这是一种预期的行为,只允许使用年/月/日选择之一,并仍然创建一个有用的日期。

Related to this post , this is the best solution I've found. 此帖相关,这是我发现的最佳解决方案。 However I should add :day, :month, :year as attr_accessible, thing I don't understand why.. (because of validation? please let me know..) 但是我应该添加:day,:month,:year astr_accessible,我不明白为什么.. (因为验证?请让我知道..)

User.rb 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

user registration form 用户注册表

<%= 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" %>

You could override the validate_on_create method, like the following: 您可以覆盖validate_on_create方法,如下所示:

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

After following Benoitr's suggestions I came up with something similar using virtual attributes. 遵循Benoitr的建议后,我想出了类似的使用虚拟属性的东西。 On the View side there are 3 separate select's (year,mon,day) inside of a 'fields_for'. 在View侧,'fields_for'中有3个单独的select(年,星期一,日)。 The data is submitted to the controller's mass assignment (no modifications in controller, see asciicasts #16 ) and then passed to a getter/setter (ie virtual attribute) in the model. 数据被提交给控制器的质量分配(在控制器中没有修改,参见asciicasts#16 ),然后传递给模型中的getter / setter(即虚拟属性)。 I'm using Rails 3.0.3, and simpleForm for the view code. 我正在使用Rails 3.0.3和simpleForm作为视图代码。

In the View: 在视图中:

<%= 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 %>

In the Model: 在模型中:

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' is the virtual attribute which sets the value of design_date in the database. 'design_date_attr'是设置数据库中design_date值的虚拟属性。 The getter passes back an hash similar to what gets submitted in the form. getter传回一个类似于表单中提交的哈希值。 The setter checks for blanks and creates a new date object and sets it and also sets the error variable. setter检查空白并创建一个新的日期对象并设置它并设置错误变量。 The custom validator :design_date_validator checks for the error instance variable and sets the errors variable. 自定义验证器:design_date_validator检查错误实例变量并设置errors变量。 I used ':base' because the variable name was not human readable and using base removes that value from the error string. 我使用':base',因为变量名不是人类可读的,使用base从错误字符串中删除该值。

A few things to refactor might be the error checking instance variable, but it seems to work at least. 重构的一些事情可能是错误检查实例变量,但它似乎至少起作用。 If anyone knows a better way to update the Date objects I'd love to hear it. 如果有人知道更新日期对象的更好方法,我很乐意听到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM