0

好的,我希望在 Rails 应用程序中实现一些相当直接的东西,但是 Rails 回调、事务和缓存正在变得更好。

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

我想告诉它project,它自己的finish_date永远不能早于finish_date它的最新版本tasks。每当task更新 a 时,如果需要,project父级应检查并调整自己的finish_date

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

我如何在 Rails 中实现它?

4

4 回答 4

1

你可以使用touch选项而不是after_save

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

class Task < ActiveRecord::Base
  belongs_to :project, :touch => :project_end_date
end
于 2012-10-10T06:57:11.350 回答
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_datesetter Project

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

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

关于这里的其他解决方案

@Joelios 解决方案建议了我所做的,只是没有覆盖设置器。问题在于,Rails 包含的方法总是使用 setter(偶数update_attributescreate)。因此,您必须自己调用update_task_date可能会忘记的方法。

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

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

希望对面临同样问题的人有所帮助。

于 2012-10-08T20:29:23.770 回答
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

结尾

于 2012-10-08T20:23:18.640 回答
0

观察者呢?您的模型将不受其他模型的知识影响,并且您将把这些知识封装到一个单独的类中。

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

于 2012-10-08T23:13:17.473 回答