8

これは、まもなく、すべての新規ユーザーがRailsについてすぐに見つけ出すものの1つです。私は、レールが実際に変更されたかどうかを確認せずに、すべてのフィールドをserializeキーワードで更新していることに気付きました。ある意味では、これは汎用フレームワークにとって賢明なことです。レールにシリアル化された属性が変更されていなくても更新されないようにする方法はありますか?

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

提案?

答えて

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 =>まだ動作します "私は更新")

class Model < ActiveRecord::Base 
    serialize :serialized_field 

    def no_serialize_update 
    true 
    end 

end 
+0

かなり有望ですね。ありがとう!オーバーライドする必要があることを知ったので、メソッド連鎖を使用する代わりに(スーパーと連動して)これを行うことができます。オーバーライド+スーパーを使用するだけで問題が発生した場合はお知らせください。 – Tabrez

+0

私もそれを試みましたが、うまくいかなかったことに気付きました。問題は、あなたがそれを上書きしてsuperを使用する前に、update_with_dirtyが既にロードされていることです(元のチェーンを介して)。 – Joris

+0

入手しました。ありがとうジョリス! – Tabrez

2

ここでは、Railsの3.1.3のための同様のソリューションです

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

は、私は今日、この問題に遭遇したとゲッターとセッターと一緒に自分のシリアライザをハッキング終わっ

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 
+0

はレール上で動作しません3.2.13 –

+0

これは動作します(RoR 3.2.18で試しました)。 あなたが追加しました: DEF含むモデルに 真 エンド をno_serialize_update 'serialize'あなたは文句を言わない、デフォルトで更新されることを望みますか? –

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 

ここで部分的な更新がうまくいくので、フィールドはデータが実際に変更されたときにのみ更新されます。

+0

素晴らしいソリューション!私は何かの欠点、おそらくパフォーマンスがあれば聞くことに興味がありますか?毎回生を解析する必要がありますか? –

1

Jorisの答えの問題は、alias_method_chainチェーンにフックし、後で実行されるすべてのチェーンを無効にすることです(トリガーが呼び出されないという問題の原因となるupdate_with_callbacksなど)。私は理解しやすくするための図を作ろうとします。あなたが直接alias_method_chainの内部の仕組みあたりupdate_with_barを変更することはできませんので、あなたは試してみてくださいupdate_with_baz

update_with_barupdate_without_barからupdate_without_fooポイントというこの

update -> update_with_foo -> update_with_bar -> update_with_baz 

お知らせなどのチェーンで始めることができ

新しいリンク(bar2)を追加してupdate_without_barを呼び出してチェーンにフックするには、

残念ながら、これはあなたに、次のチェーンを取得します:消えているので、update_with_foo

update -> update_with_bar2 -> update_with_baz 

を!

したがって、alias_method_chainを知っていると、_withメソッドを再定義することはできません。これまでのところ、私の解決策はupdate_without_dirtyを再定義してそこで属性を選択することでした。

1

解決策ではありませんが、多くの場合、私は単純に直列化された列を関連モデルに移動するだけでした。実際には、実際には意味的にはうまく適合していないことがよくありました。

関連する問題