Rails - 如何根据子属性调整父属性?

时间:2012-10-08 20:05:59

标签: ruby-on-rails ruby activerecord

好的,我希望在一个Rails应用程序中实现一些相当直接的东西我会说,但是rails回调,事务和缓存越来越好。

这就是我想要实现的目标:
两个对象projecttask。两者都有一个属性finish_date。还有project :has_many => :tasks

我想告诉project它自己的finish_date永远不会早于其finish_date上的最新tasks。每当更新task时,如果需要,project家长应检查并调整自己的finish_date

我一直在搞乱after_save回调,它并不像我想象的那样简单,因为事务和内存中同一个对象的多个版本。也许我只是做错了。

我如何在Rails中实现这一点?

4 个答案:

答案 0 :(得分:1)

以下Task模型如何:

class Task < ActiveRecord::Base
  belongs_to :project

  def after_save(task)
    self.project.finish_date = self.finish_date
  end
end

项目finish_date将设置为任务finish_date。代码假定,当前保存的任务总是与其他任何一个finished_date一样。这是一个错误的假设,只需添加适当的if语句:)

修改

我在评论中的建议是获得上面列出的Task并覆盖finish_date中的Project setter,如下所示:

class Project < ActiveRecord::Base
  has_many :tasks

  def finished_date=(new_finish_date)
    self[:finish_date] = new_finish_date if self[:finish_date] < new_finish_date
  end
end

由于我是从头开始编码的,所以我不确定项目实例是否应该在此之后保存。也许在setter中使用update_attributes可能是个更好的主意。

关于此处的其他解决方案

@Joelios解决方案建议我做了什么,但没有覆盖setter。问题是,Rails包含的方法总是使用setter(偶数update_attributescreate)。因此,你必须自己调用方法update_task_date哪个人可能会忘记。

@saverios解决方案建议使用Observer / Observable模式。所以,正如我所看到的,Project将成为其所有Task个对象的观察者。当Task发生变化时,它会调用其所有观察者,即其项目,该项目本身会更新其finish_date。它与我使用设计模式所建议的更不一样,因此使其更加困难。

可以考虑的另一个解决方案是在update中定义一个新的函数,如Project,它遍历所有连接的任务,寻找最新的finish_date。每当任务更新时,都必须调用update方法。这将是非常糟糕的设计,因为n个任务之一的每次更新都会触发n个数据库查询并减慢所有内容。

希望能帮助遇到同样问题的人。

答案 1 :(得分:1)

您可以使用触摸选项,而不是 after_save

如果将:touch选项设置为:true,则无论何时保存或销毁此对象,关联对象上的updated_at或updated_on时间戳都将设置为当前时间。您还可以指定要更新的特定时间戳属性。

class Task < ActiveRecord::Base
  belongs_to :project, :touch => :project_end_date
end

答案 2 :(得分:0)

我不确定你的问题是什么,但首先尝试没有回调:

为您的任务添加一个名为update_task_date的方法

def update_task_date(new_date)
  project.finish_date = new_date if self.project.finish_date < new_date
  project.save
  finish_date = new_date
  self.save

答案 3 :(得分:0)

观察者怎么样?您的模型将不受其他模型的了解,您将把这些知识封装到一个单独的类中。

http://api.rubyonrails.org/classes/ActiveRecord/Observer.html