好的,我希望在一个Rails应用程序中实现一些相当直接的东西我会说,但是rails回调,事务和缓存越来越好。
这就是我想要实现的目标:
两个对象project
和task
。两者都有一个属性finish_date
。还有project :has_many => :tasks
。
我想告诉project
它自己的finish_date
永远不会早于其finish_date
上的最新tasks
。每当更新task
时,如果需要,project
家长应检查并调整自己的finish_date
。
我一直在搞乱after_save
回调,它并不像我想象的那样简单,因为事务和内存中同一个对象的多个版本。也许我只是做错了。
我如何在Rails中实现这一点?
答案 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_attributes
和create
)。因此,你必须自己调用方法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