首页 文章

Rails:验证date_select中的所有字段都已选中但仍设置:prompt to true

提问于
浏览
3

当使用Rails date_select with:prompt => true时,我在提交表单时看到一些非常奇怪的行为,但没有选择所有字段 . 例如 .

提交包含1月份但在默认提示下留下的日期和年份字段的表单会导致1月1日0001传递给模型验证 . 如果验证失败并再次呈现表单,则仍会选择1月(正确),但日期设置为1(不正确) . 如果表单仅与所选年份一起提交,则将月份和日期设置为1 .

这是一种非常奇怪的行为 - 任何人都可以给我一个解决方法吗?

1 回答

  • 3

    问题与多参数分配有关 . 基本上,您希望将三个值存储到一个属性中(即 written_at ) . date_select将此作为 { 'written_at(1)' => '2009', 'written_at(2)' => '5', 'written_at(3)' => '27' } 发送到控制器 . 活动记录将这三个值打包成一个字符串,并用它初始化一个新的Date对象 .

    问题始于Date尝试使用无效日期 Date.new(2009, 0, 1) 实例化它时引发异常的事实 . Rails捕获该错误并改为实例化Time对象 . Rails中具有时区支持的Time类具有各种魔力,使其无法使用无效数据 . 这使你的一天变为1 .

    在此过程中,活动记录会丢失原始值哈希值,因为它将 written_at stuff填充到数组中并尝试从中创建Date或Time对象 . 这就是表单无法使用 written_at_before_time_cast 方法访问它的原因 .

    解决方法是在模型中添加六种方法: written_at_yearwritten_at_year=written_at_year_before_type_cast (年,月,日) . before_validation 过滤器可以重建日期并将其写入write_at .

    class Event < ActiveRecord::Base
      before_validation :reconstruct_written_at
    
      def written_at_year=(year)
        @written_at_year_before_type_cast = year
      end
    
      def written_at_year
        written_at_year_before_type_cast || written_at.year
      end
    
      def written_at_year_before_type_cast
        @written_at_year_before_type_cast
      end
    
      private
    
      def reconstruct_written_at
        written_at = Date.new(written_at_year, written_at_month, written_at_day)
      rescue ArgumentError
        written_at = nil
      end
    end
    

相关问题