10

这可能是所有新用户迟早会发现有关 Rails 的事情之一。我刚刚意识到 rails 正在使用 serialize 关键字更新所有字段,而没有检查内部是否真的发生了任何变化。在某种程度上,对于通用框架来说这是明智的做法。

但是有没有办法覆盖这种行为?如果我可以跟踪序列化字段中的值是否已更改,有没有办法防止它被推送到更新语句中?我尝试使用“update_attributes”并将散列限制为感兴趣的字段,但 rails 仍会更新所有序列化字段。

建议?

4

6 回答 6

2

这是 Rails 3.1.3 的类似解决方案。

来自:https ://sites.google.com/site/wangsnotes/ruby/ror/z00---topics/fail-to-partial-update-with-serialized-data

将以下代码放入 config/initializers/

ActiveRecord::Base.class_eval do
  class_attribute :no_serialize_update
  self.no_serialize_update = false
end

ActiveRecord::AttributeMethods::Dirty.class_eval do
  def update(*)
    if partial_updates?
      if self.no_serialize_update
        super(changed)
      else
        super(changed | (attributes.keys & self.class.serialized_attributes.keys))
      end
    else
      super
    end
  end
end
于 2012-05-18T04:05:32.623 回答
1

是的,这也困扰着我。这是我为 Rails 2.3.14(或更低版本)所做的:

# config/initializers/nopupdateserialize.rb

module ActiveRecord
  class Base
    class_attribute :no_serialize_update
    self.no_serialize_update = false
  end
end

module ActiveRecord2
  module Dirty

    def self.included(receiver)
      receiver.alias_method_chain :update, :dirty2
    end

    private 

    def update_with_dirty2
      if partial_updates?
        if self.no_serialize_update
          update_without_dirty(changed)
        else
          update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys))
        end
      else
        update_without_dirty
      end
    end

  end
end

ActiveRecord::Base.send :include, ActiveRecord2::Dirty

然后在您的控制器中使用:

model_item.no_serialize_update = true
model_item.update_attributes(params[:model_item])
model_item.increment!(:hits)
model_item.update_attribute(:nonserializedfield => "update me")

etc.

或者,如果您不希望在创建后对序列化字段进行任何更改,则在您的模型中定义它(但 update_attribute(:serialized_field => "update me" 仍然有效!)

class Model < ActiveRecord::Base
  serialize :serialized_field

  def no_serialize_update
    true
  end

end
于 2012-01-24T11:57:57.800 回答
1

我今天遇到了这个问题,最后用 getter 和 setter 破解了我自己的序列化程序。首先,我将该字段重命名为#{column}_raw,然后在模型中使用以下代码(media在我的例子中是属性)。

require 'json'

...

def media=(media)
  self.media_raw = JSON.dump(media)
end

def media
  JSON.parse(media_raw) if media_raw.present?
end

现在部分更新对我来说非常有用,并且该字段仅在数据实际更改时才更新。

于 2012-10-22T17:03:37.207 回答
1

Joris 的答案的问题在于它挂接到了alias_method_chain链中,禁用了之后完成的所有链(就像update_with_callbacks这说明了未调用触发器的问题)。我将尝试制作图表以使其更易于理解。

你可以从这样的链开始

update -> update_with_foo -> update_with_bar -> update_with_baz

注意update_without_foo指向update_with_barupdate_without_bartoupdate_with_baz

由于您不能直接update_with_bar根据您的内部工作进行修改,因此alias_method_chain可以尝试通过添加新链接 (bar2) 并调用 update_without_bar 来连接到链中,因此:

alias_method_chain :update, :bar2

不幸的是,这将为您提供以下链:

update -> update_with_bar2 -> update_with_baz

所以 update_with_foo 不见了!

所以,知道这alias_method_chain不会让你重新定义_with方法,到目前为止我的解决方案是重新定义update_without_dirty并在那里进行属性选择。

于 2012-12-07T21:22:23.163 回答
1

在许多情况下,对我来说,这不是一个很好的解决方案,但一个很好的解决方法就是将序列化的列移动到关联的模型中——通常这实际上在语义上很合适。

于 2015-05-26T14:05:58.570 回答
0

https://github.com/rails/rails/issues/8328中也有讨论。

于 2013-10-17T05:13:51.380 回答