我希望能够在我的Ruby on Rails模型中验证日期,但是当它们到达我的模型时,日期、月份和年份的值就已经转换成了错误的日期。
例如,如果我在视图中输入2009年2月31日,当我在控制器中使用Model.new(params[:model])
时,它会将其转换为“2009年3月3日”,这被我的模型认为是有效日期,但实际上是不正确的。
我希望能够在我的模型中进行此验证。有没有什么方法可以做到这一点,或者我完全错了?
我找到了这个 "日期验证",它讨论了这个问题,但从未得到解决。
我希望能够在我的Ruby on Rails模型中验证日期,但是当它们到达我的模型时,日期、月份和年份的值就已经转换成了错误的日期。
例如,如果我在视图中输入2009年2月31日,当我在控制器中使用Model.new(params[:model])
时,它会将其转换为“2009年3月3日”,这被我的模型认为是有效日期,但实际上是不正确的。
我希望能够在我的模型中进行此验证。有没有什么方法可以做到这一点,或者我完全错了?
我找到了这个 "日期验证",它讨论了这个问题,但从未得到解决。
date_select
助手来生成日期标签。您可以通过使用选择表单助手为日、月、年字段创建标签,以另一种方式完成它。像这样(我使用的示例是创建日期字段):<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %>
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %>
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %>
而在模型中,您验证日期:
attr_accessor :month, :day, :year
validate :validate_created_at
private
def convert_created_at
begin
self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i)
rescue ArgumentError
false
end
end
def validate_created_at
errors.add("Created at date", "is invalid.") unless convert_created_at
end
如果你正在寻找一个插件解决方案,我建议你看一下validates_timeliness插件。它的使用方法如下(来自github页面):class Person < ActiveRecord::Base
validates_date :date_of_birth, on_or_before: lambda { Date.current }
# or
validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date }
end
可用的验证方法列表如下:
validates_date - validate value as date
validates_time - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates - use the :timeliness key and set the type in the hash.
使用 chronic gem:
class MyModel < ActiveRecord::Base
validate :valid_date?
def valid_date?
unless Chronic.parse(from_date)
errors.add(:from_date, "is missing or invalid")
end
end
end
Chronic.parse(from_date_before_type_cast)
来获取输入的字符串值。否则,Rails会对字符串进行类型转换,因为该字段是一个datetime
字段。 - CalvinActive Record提供了 _before_type_cast
属性,其中包含类型转换之前的原始属性数据。这对于使用预转换值返回错误消息或仅执行在类型转换后不可能进行的验证非常有用。
我建议避免Daniel Von Fange的建议覆盖访问器,因为在访问器中执行验证会略微改变访问器的约定。Active Record专门针对此情况提供了一项功能。请使用它。
虽然有点晚了,但多亏了 "How do I validate a date in rails?" ,我成功编写了这个验证器,希望它对某些人有用:
在你的 model.rb
中:
validate :date_field_must_be_a_date_or_blank
# If your field is called :date_field, use :date_field_before_type_cast
def date_field_must_be_a_date_or_blank
date_field_before_type_cast.to_date
rescue ArgumentError
errors.add(:birthday, :invalid)
end
由于你需要在模型中将日期字符串转换成日期之前处理它,我建议覆盖该字段的访问器。
假设你的日期字段是published_date
。请将以下内容添加到你的模型对象中:
def published_date=(value)
# do sanity checking here
# then hand it back to rails to convert and store
self.write_attribute(:published_date, value)
end
Here's a non-chronic answer..
class Pimping < ActiveRecord::Base
validate :valid_date?
def valid_date?
if scheduled_on.present?
unless scheduled_on.is_a?(Time)
errors.add(:scheduled_on, "Is an invalid date.")
end
end
end
"1/1/2013".is_a?(Date)
- 日期来自用户输入,因此它被作为字符串提供。我需要先将其解析为日期吗? - MohamadDate.parse("1/1/2013").is_a?(Date)
,但是如果它根本无法解析,那么它也很可能不是一个日期。 - Trip# Set parameters
year = params[:date][:year].to_i
month = params[:date][:month].to_i
mday = params[:date][:mday].to_i
hour = params[:date][:hour].to_i
minute = params[:date][:minute].to_i
# Validate date, time and hour
valid_date = Date.valid_date? year, month, mday
valid_hour = (0..23).to_a.include? hour
valid_minute = (0..59).to_a.include? minute
valid_time = valid_hour && valid_minute
# Check if parameters are valid and generate appropriate date
if valid_date && valid_time
second = 0
offset = '0'
DateTime.civil(year, month, mday, hour, minute, second, offset)
else
# Some fallback if you want like ...
DateTime.current.utc
end
validate :birthday_must_be_a_date
def birthday_must_be_a_date
birthday&.to_date || errors.add(:birthday, :blank)
rescue Date::Error
errors.add(:birthday, :invalid)
end
你尝试过使用validates_date_time
插件吗?