分享

Ruby on Rails处理遗留数据库的问题

 ricosxf 2015-10-08

因为RoR有自己的一套命名机制,而且存在命名空间污染的问题,在处理遗留数据库的时候往往产生问题。比如下面的两例:一个是errors字段,一个是type字段。

>> oo.save
NoMethodError: undefined method `clear' for 0:Fixnum
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/validations.rb:1027:in `valid_without_callbacks?'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/callbacks.rb:286:in `valid?'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/validations.rb:1008:in `save_without_dirty'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/dirty.rb:79:in `save_without_transactions'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:179:in `send'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:179:in `with_transaction_returning_status'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in `transaction'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:129:in `transaction'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:138:in `transaction'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:178:in `with_transaction_returning_status'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:146:in `save'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:158:in `rollback_active_record_state!'
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/transactions.rb:146:in `save'

这个错误莫名其妙,它报告说数字0没有方法clear,0的ruby类型为Fixnum。查询google也找不到结果。查看validations.rb发现代码如下:

1026    def valid?
1027      errors.clear

1044    def errors
1045      @errors ||= Errors.new(self)
1046    end

通过再次查看数据库表定义发现了错误的原因。本来是应该执行@errors实例的clear方法,可是因为数据库中有errors字段,变成了执行该属性的clear方法。通过查资料,看源码,都没找到在ror中如何自定义数据库字段的映射。最多只有自定义数据库表名,比如通过set_table_name。经过多次尝试,比如更改has_attribute?的实现
    def has_attribute?(attr_name)
      if attr_name == "errors"
    return false;
      end
      @attributes.has_key?(attr_name.to_s)
    end

或者更改attributes_from_column_definition的实现,

def attributes_from_column_definition
  self.class.columns.inject({}) do |attributes, column|
    attributes[column.name] = column.default unless column.name == self.class.primary_key or column.name == "errors"
    attributes
  end
end

结果都不行。这样虽然没有了errors属性,可是保存的时候报错ActiveRecord::MissingAttributeError: missing attribute: errors。最后发现,下面的修改可以工作。

class SmsOut < ActiveRecord::Base
      def errors
        super
      end
end

>> oo.errors
=> #<ActiveRecord::Errors:0x47fa644 @errors={}, @base=#<SmsOut id: 1, recipient: "123456", text: "hello,world", create_date: "2009-03-27 14:59:42", originator: "", encoding: "U", status_report: 0, flash_sms: 0, src_port: -1, dst_port: -1, sent_date: nil, ref_no: nil, priority: 0, status: "U", errors: 0, gateway_id: "*">>
>> oo.read_attribute("errors")
=> 0

 

另外一个表出现了另一个错误:

>> SmsIn.find_by_originator("123456")

ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'I'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite SmsIn.inheritance_column to use another column for that information.
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:1579:in `instantiate'
...
        from f:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/base.rb:1800:in `method_missing'

  通过查看SmsIn的字段果然发现有个字段名字为"type",ROR默认用这个字段来处理继承。

>> SmsIn.column_names
=> ["id", "process", "originator", "type", "encoding", "message_date", "receive_date", "text", "original_ref_no", "original_receive_date", "gateway_id"]

因为数据库结构是不能更改的,只能更改ror的定义。

class SmsIn < ActiveRecord::Base
    self.inheritance_column = ""
end

这样可以正常使用SmsIn模型了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多