Rails 使用 serialize 在 db 中储存 hash 对象时遇到的一个小问题

adamshen · 发布于 2016年12月12日 · 634 次阅读
A908ae

更改attibutes方法生成出来的hash对象再save以后,会改变数据库里实际所存储的值

class Request < ActiveRecord::Base
  serialize :options
end

request = Request.last
=> options: {level: 7}

request_hash = request.attributes
request_hash['options'] = {level: 0}
request.save
request.reload

request  => options: {level: 0}

造成这个问题的原因是attributes方法得到的hash里,key options所指向的obj和ActiveRecord::Attribute::FromDatabase实例里value变量所指向的obj是同一个obj

request.options.equal?(request.attributes['options'])
=> true

AD的instance里用attributes变量暂存record的值

# From: /opt/rubies/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0.1/lib/active_record/attribute_methods.rb @ line 275:
def attributes
  @attributes.to_hash
end

它是一个ActiveRecord::AttributeSet的实例

每一个column的value用ActiveRecord::Attribute::FromDatabase的实例来存储

request.instance_variable_get(:@attributes)

=> #<ActiveRecord::AttributeSet:0x00000008d8e930
 @attributes=
  #<ActiveRecord::LazyAttributeHash:0x00000008d8e980
   @additional_types={},
   @delegate_hash=
    {"options"=>
      #<ActiveRecord::Attribute::FromDatabase:0x00000008d86de8
       @name="options",
       @original_attribute=nil,
       @type=#<ActiveModel::Type::Text:0x00000000f73668 @limit=nil, @precision=nil, @scale=nil>,
       @value=
        {:level => 0}
     }

当使用attributes方法时,只是取出ActiveRecord::Attribute::FromDatabase实例的value变量指向的对象,没有进行dup,所以产生了上述问题

# From: /opt/rubies/ruby-2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.0.1/lib/active_record/attribute_set.rb @ line 21:
def to_hash
  initialized_attributes.transform_values(&:value)
end
共收到 0 条回复
A908ae adamshen 关闭了讨论 12月13日 11:26
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册