扶手:更新对象从相关对象的回调中
我有一个名为父类考试,并且都有得分类的实例。我想修改的考试实例的属性时,相关联的得分中的一个被保存。我剥夺了所有的类到这个非常简单的例子,它看起来愚蠢,但足以说明其最基本的形式问题。下面是类。
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 ofself.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).