У меня есть «родительский класс» с именем Exam, у которого есть много экземпляров класса Score. Я хочу изменить атрибут в экземпляре экзамена, когда один из связанных баллов сохранен. Я сократил все классы до этого очень простого примера, который выглядит глупо, но иллюстрирует проблему в ее самой простой форме. Вот классы.
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
Затем я запускаю следующий тест:
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
Комментарии в коде уже иллюстрируют проблему: обновление атрибута имени объекта экзамена не происходит. Напомним, что процедура trigger_score_saved выполняется, но вновь установленное значение не является тем, которое в конечном итоге сохраняется в базе данных.
Если я определю обратный вызов before_save :trigger_score_saved
для самого объекта экзамена, атрибут имени действительно будет обновлен правильно. Так что, похоже, что-то связано с тем, что происходит каскадное сохранение и что, возможно, родительский объект экзамена, с которого началось сохранение, отличается от объекта Score.exam, который я пытаюсь изменить. 1010 *
Может кто-нибудь объяснить, что здесь происходит и как я могу успешно обновить атрибут родительского объекта из обратного вызова «дочернего объекта»?
Примечания:
- Я использую Rails 3 и Ruby 1.9.2
- Я пробовал
update_attribute(:name => "#{name}!")
вместо self.name = "#{name}!"
, но оба имеют одинаковый эффект