扶手:更新对象从相关对象的回调中

问题描述:

我有一个名为父类考试,并且都有得分类的实例。我想修改的考试实例的属性时,相关联的得分中的一个被保存。我剥夺了所有的类到这个非常简单的例子,它看起来愚蠢,但足以说明其最基本的形式问题。下面是类。

I have a "parent class" called Exam and that has many instances of a Score class. I want to modify an attribute on the Exam instance when one of the associated scores is saved. I stripped all the classes down to this very simple example, which looks stupid, but illustrates the problem in its most basic form. Here are the classes.

class Exam < ActiveRecord::Base
  has_many :scores

  def score_saved
    # self.name is now "Software Engineering"
    self.name = "#{name}!"
    # self.name is now "Software Engineering!"
  end
end

class Score < ActiveRecord::Base
  belongs_to :exam
  belongs_to :course

  before_save :trigger_score_saved

  def trigger_score_saved
    exam.score_saved unless exam.nil?
  end
end

然后我运行下面的测试:

Then I run the following test:

class ExamTest < ActiveSupport::TestCase
  test "create new exam" do
    exam = Exam.new(:name => "Software Engineering 1")
    score = exam.scores.build(:grade => 80, :course => courses(:one))
    exam.save

    # self.name is still "Software Engineering" here
    assert_equal "Software Engineering 1!", exam.name 
  end 
end

在code的评论已经说明问题:考试对象的名称属性的更新不会发生。提醒一下,所述trigger_score_saved进程内执行,但新设置的值不是真实最终保存到数据库中的之一。 如果我定义了一个 before_save:考试对象本身trigger_score_saved 回调,name属性的确实的得到正确更新。因此,似乎有什么东西做的事实,有一个级联保存回事,在其上保存启动,也许父考试的对象是我想要修改,该值score.exam对象不同

The comments in the code already illustrate the problem: the update of the name attribute of the exam object does not take place. Mind you, the trigger_score_saved proc is executed, but the newly set value is not the one that’s eventually saved to the database. If I define a before_save :trigger_score_saved callback on the exam object itself, the name attribute does get updated correctly. So it seems to have something to do with the fact that there’s a cascading save going on and that maybe the parent exam object on which the save started is different from the score.exam object that I’m trying to modify the value of.

谁能解释一下是怎么回事,我怎么能成功地从一个子对象的回调中更新父对象的属性?

Can anyone explain what’s going on here and how I can successfully update a parent object’s attribute from within the callback of a "child object"?

注:

  • 在我使用的Rails 3和Ruby 1.9.2
  • 在我试过 update_attribute(:名称=&gt;中#{名}!)的,而不是 self.name =#{名字}!,但两者具有相同的效果
  • I use Rails 3 and Ruby 1.9.2
  • I’ve tried update_attribute(:name => "#{name}!") instead of self.name = "#{name}!", but both have the same effect

我已经做了一些调查和事实证明,我的问题其实很简单地解决了指定:inverse_of 在相关协会的属性:

I've done some more investigating and as it turns out, my problem is actually very simply solved by specifying the :inverse_of attribute on the relevant associations:

class Exam < ActiveRecord::Base
  has_many :scores, :inverse_of => :exam
  ...
end

class Score < ActiveRecord::Base
  belongs_to :exam, :inverse_of => :scores
  ...
end

这样 exam.scores.first.exam 的完全相同的实例作为考试,因为:inverse_of 属性告诉Rails使用同一个对象实例在内存

This way exam.scores.first.exam is the exact same instance as exam, since the :inverse_of attribute tells Rails to use the same object instance in memory!

此外,我要考试要更新上任何得分任何CRUD操作,所以还当我删除了exam.scores收集的分数。这是联想回调像:after_remove 派上用场

Additionally, I want the exam to be updated on any CRUD action on any score, so also when I delete a score from the exam.scores collection. This is where association callbacks like :after_remove come in handy.

因此​​,所有这些工具了我的腰带,看来我可以继续前进,即使没有身份地图(虽然,我肯定确实看到有那些Rails中的一个值)。

So all with these tools up my belt, it seems that I can move forward even without an Identity Map (even though, I definitely do see the value in having one of those in Rails).