このような ActiveRecord クラスがあります。
class Foo
belongs_to :bar, autosave: true
before_save :modify_bar
...
end
ログを記録すると、 が変更されていることがわかりますbar
が、その変更は保存されません。どうしたの?
このような ActiveRecord クラスがあります。
class Foo
belongs_to :bar, autosave: true
before_save :modify_bar
...
end
ログを記録すると、 が変更されていることがわかりますbar
が、その変更は保存されません。どうしたの?
ここでの問題は、通常のコールバックをautosave: true
設定するだけで、コールバックが作成された順序で実行されることです。**before_save
before_save
bar
したがって、変更のないを保存しようとしてから、を呼び出しますmodify_bar
。
解決策はmodify_bar
、自動保存の前にコールバックが実行されるようにすることです。
これを行う 1 つの方法は、prepend
オプションを使用することです。
class Foo
belongs_to :bar, autosave: true
before_save :modify_bar, prepend: true
...
end
別の方法は、before_save
ステートメントを の前に置くことbelongs_to
です。
bar
もう 1 つの方法は、メソッドの最後で明示的に保存し、オプションをまったくmodify_bar
使用しないことです。autosave
役立つブログ記事を提供してくれた Danny Burkes に感謝します。
** また、すべてのafter_validation
コールバックの後、コールバックの前に実行されbefore_create
ます - docs を参照してください。
このようなコールバックの順序を確認する 1 つの方法を次に示します。
describe "sequence of callbacks" do
let(:sequence_checker) { SequenceChecker.new }
before :each do
foo.stub(:bar).and_return(sequence_checker)
end
it "modifies bar before saving it" do
# Run the before_save callbacks and halt before actually saving
foo.run_callbacks(:save) { false }
# Test one of the following
#
# If only these methods should have been called
expect(sequence_checker.called_methods).to eq(%w[modify save])
# If there may be other methods called in between
expect(sequence_checker.received_in_order?('modify', 'save')).to be_true
end
end
このサポート クラスの使用:
class SequenceChecker
attr_accessor :called_methods
def initialize
self.called_methods = []
end
def method_missing(method_name, *args)
called_methods << method_name.to_s
end
def received_in_order?(*expected_methods)
expected_methods.map!(&:to_s)
called_methods & expected_methods == expected_methods
end
end
上記の答えは(明らかに)あなたの解決策です。でも:
を使用して:autosave
も問題ありませんが、外部関連付けの変更がコールバックの仕事だとは思いません。私はあなたについて話している:modify_bar
。この投稿で 見事に説明されているように、私は別のオブジェクトを使用して複数のモデルを一度に保存することを好みます。物事がより複雑になると、それはあなたの人生を本当に簡素化し、テストをはるかに簡単にします.
この状況では、これはコントローラーまたはサービス オブジェクトから実行される可能性があります。