为什么ActiveRecord的自动保存不能用于我的关联?

Nat*_*ong 11 ruby ruby-on-rails callback rails-activerecord

我有一个看起来像这样的ActiveRecord类.

class Foo
  belongs_to :bar, autosave: true
  before_save :modify_bar
  ...
end
Run Code Online (Sandbox Code Playgroud)

如果我做一些日志记录,我看到bar正在修改,但它的更改不会保存.怎么了?

Nat*_*ong 24

这里的问题是autosave: true简单地设置一个正常的before_save回调,并且before_save回调按照它们被创建的顺序运行.**

因此,它试图保存bar没有变化的,然后调用modify_bar.

解决方案是确保modify_bar回调在自动保存之前运行.

一种方法是使用该prepend选项.

class Foo
  belongs_to :bar, autosave: true
  before_save :modify_bar, prepend: true
  ...
end
Run Code Online (Sandbox Code Playgroud)

另一种方法是将before_save声明置于之前belongs_to.

另一种方法是barmodify_bar方法结束时显式保存,而根本不使用该autosave选项.

感谢Danny Burkes的有用博客文章.

**此外,它们在所有after_validation回调之后和任何before_create回调之前运行- 请参阅文档.


更新

这是检查此类回调顺序的一种方法.

  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
Run Code Online (Sandbox Code Playgroud)

使用此支持类:

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
Run Code Online (Sandbox Code Playgroud)