代码之家  ›  专栏  ›  技术社区  ›  Pascal Lindelauf

Rails:从关联对象的回调中更新对象

  •  2
  • Pascal Lindelauf  · 技术社区  · 14 年前

    我有一个父类叫做考试,它有许多分数类的例子。我想在保存一个相关分数时修改考试实例的属性。我将所有的类都简化为这个非常简单的示例,它看起来很愚蠢,但以最基本的形式说明了这个问题。这是课程。

    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
    

    代码中的注释已经说明了这个问题:考试对象的name属性没有更新。请注意,触发器“score_saved proc”被执行,但新设置的值不是“s”最终保存到数据库的值。 如果我定义了 before_save :trigger_score_saved 对检查对象本身的回调,名称属性 正确更新。因此,这似乎与一个正在进行的级联存储有关,并且存储开始的父级检查对象可能与我试图修改的score.exam对象不同。

    有人能解释一下这里发生了什么,以及我如何在子对象的回调中成功地更新父对象的属性吗?

    笔记:

    • 我使用Rails 3和Ruby 1.9.2
    • 我试过了 update_attribute(:name => "#{name}!") 而不是 self.name = "#{name}!" 但两者效果相同
    2 回复  |  直到 14 年前
        1
  •  2
  •   François Beausoleil    14 年前

    正如您所猜测的,内存中有不同的考试类实例引用相同的db行。您可以调用reload来刷新,或者等待标识工作在发布的Rails版本中完成。

    对身份图的一些引用:

        2
  •  2
  •   Pascal Lindelauf    14 年前

    我做了更多的调查,结果发现,通过指定 :inverse_of 相关关联的属性:

    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 完全相同的实例 exam ,因为 :反转 属性告诉Rails在内存中使用相同的对象实例!

    此外,我希望在任何分数的任何积垢操作上更新考试,所以当我从exam.scores集合中删除一个分数时也是如此。这就是关联回调的地方 :after_remove 来帮忙吧。

    所以,所有这些工具在我的腰带上,似乎我可以向前移动,即使没有一个身份图(尽管,我确实看到了在Rails中拥有这些工具的价值)。