我正在为模型使用类似于以下结构的东西:
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
我认为问题在于它每次都会对它应用to_yaml并最终破坏格式
答案 0 :(得分: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中,它确实比较了值,这个问题就消失了。
另外,如果我理解你的担忧,我认为这不是你实际问题的原因,我接下来会讨论这个问题。
这我不太确定。我知道你的Hash被序列化为yaml字符串 - 这就是序列化的工作方式。如果你展示一个更具体的例子,这将是“作为一个字符串”回来的问题,这将是非常有帮助的。也许你是对的,它一遍又一遍地贬低你的价值,但这会让我在正常使用下感到惊讶。