【问题标题】:Rails chronic gem not validating as expectedRails 长期 gem 未按预期验证
【发布时间】:2012-11-09 04:38:14
【问题描述】:

我正在尝试将日期“03/20/1985”输入到名为“生日”的文本字段中,并将其插入到列类型为“日期”的数据库字段中。

当我输入10/20/1985 时,我收到错误“生日无效”,但当我输入20/10/1985 时,它工作得很好。

从我一直阅读的所有文档来看,chronic 应该将“10/20/1985”解析为 mm/dd/yyyy,但它似乎将其解析为 dd/mm/yyyy。

我怎样才能将日期解析为 mm/dd/yyyy?

/models/user.rb

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login]

  # Virtual attribute for authenticating by either username or email
  # This is in addition to a real persisted field like 'username'
  attr_accessor :login

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :login, :first_name, :last_name, :home_phone, :cell_phone, :work_phone, :birthday, :home_address, :work_address, :position, :company

  validate :birthday_is_date
  validate :position, :presence => true

  require 'chronic'

  # validate the birthday format
  def birthday_is_date 
    errors.add(:birthday, "is invalid") unless Chronic.parse(birthday)
  end

  # validates email or username when logging in
  def self.find_first_by_auth_conditions(warden_conditions)
    conditions = warden_conditions.dup
    if login = conditions.delete(:login)
      where(conditions).where(["lower(username) = :value OR lower(email) = :value", { :value => login.downcase }]).first
    else
      where(conditions).first
    end
  end

end

【问题讨论】:

  • 数据库中的值是什么样的?
  • 如果我使用 '20/10/1985`,数据库显示 2012-10-20,但如果我使用 10/20/1985,数据库不会更新任何内容,我会收到错误消息。
  • 不确定是否重要,但这是一个设计模型
  • 我只是想知道数据库表的构造是否奇怪,或者其他什么,但它似乎是合法的......
  • 你在用 i18n 做什么吗?

标签: ruby-on-rails ruby ruby-on-rails-3 chronic


【解决方案1】:

如果值作为日期存储在数据库中,Rails 将在赋值时将值从字符串强制转换为 Ruby 日期。我认为它可能使用了内置的Date.parse方法(docs):

Date.parse "2012-10-20"
# => #<Date 2012-10-20 ...>

Date.parse "20-10-2012"
# => #<Date 2012-10-20 ...>

Date.parse "10-20-2012"
# => ArgumentError: invalid date

在这种情况下,您希望避免强制并使用原始字符串来使用 Chronic 进行解析。这是virtual attributes 的理想用例。有几种方法可以做到这一点,这样的事情应该可以帮助你开始

class User < ActiveRecord::Base
  validate :birthday_is_date

  # an explicit implementation
  def birthday_string
    if @birthday_string
      @birthday_string
    elsif birthday.present?
      birthday.strftime("%d-%m-%Y")
    else
      ""
    end
  end

  # a shorter implementation
  def birthday_string
    @birthday_string || birthday.try(:strftime, "%d-%m-%Y")
  end

  def birthday_string=(value)
    @birthday_string = value
    self.birthday = parse_birthday
  end

  private

  def birthday_is_date
    errors.add(:birthday_string, "is invalid") unless parse_birthday
  end

  def parse_birthday
    Chronic.parse(birthday_string)
  end
end

然后在您的表单中使用birthday_string,而不是birthday

【讨论】:

  • 这很好用,除非我输入日期1/5/1980(1980 年 1 月 5 日),它将它作为 1980-05-01 保存到数据库中,然后在我的应用程序中显示为 05/01/1980。我怎样才能让它明白我希望 1/5/1980 实际上表示 1980 年 1 月 5 日而不是 1980 年 5 月 1 日?
  • 实际上它现在似乎正在工作。我注意到日志中有一条关于无法批量分配生日字符串的警告,因此我将 :birthday_string 添加到 attr_accessible 并且现在效果很好。
  • 我刚注意到的一件事是,如果生日为空,我的编辑页面就会出现undefined method 'strftime'。你知道我如何避免这个生日为空的错误吗?
  • 您只需要在birthday_string 方法中检查一个零birthday。我为我的答案添加了几种可能的方法。
【解决方案2】:

两者都对我有用,也许你需要更新。

还有:

Chronic.parse '2/10/1985'
#=> 1985-02-10 12:00:00 +0800
Chronic.parse '2/10/1985', :endian_precedence => :little
#=> 1985-10-02 12:00:00 +0800

【讨论】:

  • 那时可能不会。这可能是语言环境的问题,但我不确定。
  • 我的 Rails 控制台与我的实际应用程序有什么不同?
  • 可能是环境?听起来有些不同,但我不能说。
猜你喜欢
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-29
  • 2018-01-27
  • 2012-09-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多