10

これはおそらく、すべての新規ユーザーが遅かれ早かれ Rails について知ることの 1 つです。内部で実際に何かが変更されたかどうかを確認せずに、レールが serialize キーワードですべてのフィールドを更新していることに気付きました。ある意味で、それは一般的なフレームワークに対して行うのが賢明なことです。

しかし、この動作を無効にする方法はありますか? シリアル化されたフィールドの値が変更されたかどうかを追跡できる場合、更新ステートメントで値がプッシュされないようにする方法はありますか? 「update_attributes」を使用して、ハッシュを対象のフィールドに制限しようとしましたが、レールはまだシリアル化されたすべてのフィールドを更新します。

提案?

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

Jorisの答えの問題は、それがチェーンにフックされ、alias_method_chainその後に行われたすべてのチェーンを無効にすることです(update_with_callbacksトリガーが呼び出されないという問題を説明するように)。分かりやすいように図にしてみます。

このようなチェーンから始めることができます

update -> update_with_foo -> update_with_bar -> update_with_baz

とをupdate_without_foo指していることに注意してくださいupdate_with_barupdate_without_barupdate_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

はい、それも私を悩ませていました。これは、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

私は今日この問題に遭遇し、ゲッターとセッターと一緒に自分のシリアライザーをハッキングすることになりました。まず、フィールドの名前を に変更してから#{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 に答える
0

https://github.com/rails/rails/issues/8328にも議論があります。

于 2013-10-17T05:13:51.380 に答える