ahs*_*ele 1 validation ruby-on-rails
因为 RoR 不提供validate_on_destroy,所以我本质上是通过使用before_destroy
回调来实现的。
使用before_destory
可以防止已删除的项目。effort_logged?
下面的实现不起作用,因为当没有记录时,我想删除该项目及其所有依赖项。只要before_destroy
按照下面的方式实施,我就无法这样做。
如果我了解在调用父级方法之前如何删除:dependent => :destroy
与依赖子级相关的工作。如果我的假设是正确的,那么访问方法中的子项是否会导致它们不被删除?有没有更好的方法来检查是否可以根据其子级删除父级?before_destroy
before_destroy
effort_logged?
除了对 RoR 如何工作的好奇之外,我的目标是通过以下两项测试:
鉴于下面列出的所有内容,我希望这两项测试都能通过。
项目模型
class Project < ActiveRecord::Base
has_many :project_phases, :dependent => :destroy
def before_destroy
if effort_logged?
errors.add_to_base("A project with effort logged cannot be deleted")
false
else
true
end
end
def effort_logged?
project_phases.each do |project_phase|
project_phase.deliverables.each do |deliverable|
if (deliverable.effort_logged?)
return true
end
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
项目阶段模型
class ProjectPhase < ActiveRecord::Base
belongs_to :project
has_many :deliverables, :dependent => :destroy
end
Run Code Online (Sandbox Code Playgroud)
可交付模型
class Deliverable < ActiveRecord::Base
has_many :effort_logs, :dependent => :destroy
def effort_logged?
total_effort_logged != 0
end
def total_effort_logged
effort_logs.to_a.sum {|log| log.duration}
end
end
Run Code Online (Sandbox Code Playgroud)
努力日志模型
class EffortLog < ActiveRecord::Base
belongs_to :deliverable
end
Run Code Online (Sandbox Code Playgroud)
测试无法删除已记录工作量的项目
test "cannot delete project with effort logged" do
project = projects(:ProjectOne)
assert !project.destroy, "#{project.errors.full_messages.to_sentence}"
end
Run Code Online (Sandbox Code Playgroud)
测试何时不进行任何记录的项目删除会删除依赖项
test "when no effort logged project deletion deletes dependents" do
project = projects(:ProjectNoEffort)
# all phases of the project
project_phases = project.project_phases
# all deliverables of all phases of the project
project_phases_deliverables = {}
# all effort logs of all deliverables of the project
deliverables_effort_logs = {}
project_phases.each do |project_phase|
project_phases_deliverables[project_phase.name + "-" + project_phase.id.to_s] =
project_phase.deliverables
end
project_phases_deliverables.each { |project_phase, project_phase_deliverables|
project_phase_deliverables.each do |deliverable|
deliverables_effort_logs[deliverable.name + "-" + deliverable.id.to_s] =
deliverable.effort_logs
end
}
project.destroy
assert_equal(0, project_phases.count,
"Project phases still exist for the deleted project")
project_phases_deliverables.each { |project_phase, project_phases_deliverables|
assert_equal(0, project_phases_deliverables.count,
"Deliverables still exist for the project phase \"" + project_phase + "\"")
}
deliverables_effort_logs.each { |deliverable, deliverables_effort_logs|
assert_equal(0, deliverables_effort_logs.count,
"Effort logs still exist for the deliverable \"" + deliverable + "\"")
}
end
Run Code Online (Sandbox Code Playgroud)
我发现这个问题是因为我遇到了同样的问题。事实证明,回调的顺序很重要。当您在 Rails 中定义关系时,该:dependent
选项实际上会在幕后创建回调。如果您在关系之后before_destroy
定义回调,那么直到关系被销毁之后才会调用您的回调。
解决方案是更改回调的顺序,以便您首先定义依赖于仍然存在的关系的回调。关系定义应在之后进行。
您的代码应该看起来更像这样:
class Project < ActiveRecord::Base
# this must come BEFORE the call to has_many
before_destroy :ensure_no_effort_logged
# this must come AFTER the call to before_destroy
has_many :project_phases, :dependent => :destroy
# this can be placed anywhere in the class
def ensure_no_effort_logged
if effort_logged?
errors.add_to_base("A project with effort logged cannot be deleted")
false
else
true
end
end
end
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4071 次 |
最近记录: |