Rails:如何处理数据库中现有的无效日期?

时间:2013-08-01 16:51:11

标签: mysql ruby-on-rails date activerecord ruby-on-rails-3.2

首先,这与我的另一个问题直接相关: How to gracefully handle "Mysql2::Error: Invalid date" in ActiveRecord?

但我仍然不想跳过编写修复日期的迁移的所有循环。这不会是无效日期的最后一个表,我需要一些更通用的方法。

所以我们走了:

我正在使用遗留的MySQL数据库,其中包含无效日期,有时像2010-01-00或0000-04-25 ... Rails不会加载此类记录(旧版本的Rails确实如此)。

我不想(也不能)手动或自动更正这些日期。应该由这些记录的作者来纠正这些日期。旧系统是一个允许这种烦恼的PHP应用程序。 Rails应用程序应该/只会阻止用户保存记录,直到日期有效。

问题似乎不在Rails本身内部,而是在rails mysql gem的.so库中更深层次。

所以我的问题不是关于如何验证日期或如何插入无效日期。我不想那样做,而且遍布stackoverflow和互联网的其余部分都有很多答案。我的问题是如何从数据库中已经存在的MySQL中读取无效日期而不将Rails爆炸成1000个小块......

列类型为DATETIME,我不确定转换为字符串是否有帮助,因为Rails会在任何与ActiveRecord相关的解析开始之前窒息。

这是确切的错误和回溯:

$ rails c
Loading development environment (Rails 3.2.13)
irb(main):001:0> Poll.first
  Poll Load (0.5ms)  SELECT `polls`.* FROM `polls` LIMIT 1
Mysql2::Error: Invalid date: 2003-00-01 00:00:00
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `each'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `to_a'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:216:in `exec_query'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/mysql2_adapter.rb:224:in `select'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:38:in `find_by_sql'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/explain.rb:41:in `logging_query_plan'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:37:in `find_by_sql'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:171:in `exec_queries'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:160:in `to_a'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/explain.rb:34:in `logging_query_plan'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation.rb:159:in `to_a'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation/finder_methods.rb:380:in `find_first'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/relation/finder_methods.rb:122:in `first'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:5:in `__send__'
        from /home/kakra/.gem/ruby/1.8/gems/activerecord-3.2.13/lib/active_record/querying.rb:5:in `first'
        from (irb):1

即使我执行Poll.first.title,回溯仍保持不变,因此某些日期永远不会到达IRB中的任何输出例程,因此永远不应该被解析。因此,在类型转换之前使用值的建议无济于事。

2 个答案:

答案 0 :(得分:2)

我认为对我有用的最简单的解决方案是设置 database.yml 文件 cast:false ,例如发展部分

development
  <<: *default
  adapter: mysql2    
  (... some other settings ...)
  cast: false

答案 1 :(得分:0)

试试这个

ActiveRecord :: AttributeMethods :: BeforeTypeCast提供了一种在类型转换和反序列化之前读取属性值的方法。

http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html