使用可序列化列并使用不正确的值重新保存

时间:2014-07-17 01:19:27

标签: ruby-on-rails activerecord serialization ruby-on-rails-3.2

我正在为模型使用类似于以下结构的东西:

class User < ActiveRecord::Base
  serialize :frag, Hash
  attr_accessible :name, :frag
end

frag值是一个文本字段,是一个预先计算好的哈希,然后我们可以通过to_json进行渲染。我遇到的问题是,当我保存时,它会重新保留frag值,即使它没有改变,并且很明显存在一些损坏,因为它有时会以String而不是Hash的形式返回。

1.9.3-p547 :004 > u=User.first
  User Load (0.2ms)  SELECT "users".* FROM "users" LIMIT 1
 => #<User id: 1, name: "Blax", frag: {:name=>"Black Monday"}, created_at: "2014-07-17 01:04:27", updated_at: "2014-07-17 01:10:52"> 
1.9.3-p547 :005 > u.name="Joe"

   (0.1ms)  begin transaction
   (0.5ms)  UPDATE "users" SET "name" = 'Joe', "updated_at" = '2014-07-17 01:11:29.281133', "frag" = '---
:name: Black Monday
' WHERE "users"."id" = 1
   (1.3ms)  commit transaction

有没有办法告诉rails绕过保存未更改的值,以便它不会尝试重新保存碎片值,如果它没有改变?这就是我想要创建一个单独的类来管理这个缓存的值。

我真的想要这个:

   (0.5ms)  UPDATE "users" SET "name" = 'Joe', "updated_at" = '2014-07-17 01:11:29.281133', "frag" = '---
:name: Black Monday
' WHERE "users"."id" = 1

是这样的:

   (0.5ms)  UPDATE "users" SET "name" = 'Joe', "updated_at" = '2014-07-17 01:11:29.281133'
   'WHERE "users"."id" = 1

THX

编辑1

我认为问题在于它每次都会对它应用to_yaml并最终破坏格式

1 个答案:

答案 0 :(得分:1)

你的问题中有两件事情。

1。每次保存时都会更新序列化值。

这实际上是预期的行为,你可以阅读它in the source。但是,让我详细说明一下:

ActiveRecord知道属性是否已更改的方式是因为使用了setter方法。

  user = User.first
  user.frag = {new: 'hash'} # this calls the setter method User#frag=
  user.frag_changed?        # => true

但是,例如,在序列化哈希时,frag很可能在没有调用setter的情况下更改

  user = User.create name: 'new', frag: {a: 'hash'}
  user.frag # => {a: 'hash'}
  user.frag[:that] = 'changed'
  user.frag # => {a: 'hash', that: 'changed'}

因此。由于这个问题,并且(我猜)因为与当前db值进行完全比较的性能被认为太贵了,所以你所看到的是非常有意的。

仅供参考,在Rails 4.1中,它确实比较了值,这个问题就消失了。

另外,如果我理解你的担忧,我认为这不是你实际问题的原因,我接下来会讨论这个问题。

2。您的序列化值“有时会以字符串形式返回”

这我不太确定。我知道你的Hash被序列化为yaml字符串 - 这就是序列化的工作方式。如果你展示一个更具体的例子,这将是“作为一个字符串”回来的问题,这将是非常有帮助的。也许你是对的,它一遍又一遍地贬低你的价值,但这会让我在正常使用下感到惊讶。