我希望在从has_many
关联中删除对象之前运行一些代码。
我认为我可以使用before_remove
回调执行此操作但是由于某种原因,这不会触发,我不明白为什么。
class Person < ActiveRecord::Base
has_many :limbs, before_remove: :print_message
def print_message
puts 'removing a limb'
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
虽然这段代码应该在肢体破坏期间打印“移除肢体”,但事实并非如此。
p = Person.create;
l = Limb.create person: p;
p.limbs.first.destroy
# SQL (2.1ms) DELETE FROM "limbs" WHERE "limbs"."id" = ? [["id", 5]]
# => #<Limb id: 5, person_id: 3, created_at: "2012-01-17 11:28:01", updated_at: "2012-01-17 11:28:01">
为什么此destroy
操作不会导致print_message
方法运行?
编辑 - 这个before_remove
回调是否存在?
许多人都在询问是否存在此回调。虽然我可以找到很少的进一步引用,但它在Rails文档中有记录:
这是一个关联回调,而不是根ActiveRecord回调
编辑2 - 为什么不在肢体上使用before_destroy
?
有些人问为什么我没有在Limb上使用before_destroy
回调。原因是我希望人检查是否有最小数量的肢体,并且最后一个肢体永远不会被摧毁。这是原始问题:
How do you ensure that has_many always "has a minimum"?
答案 0 :(得分:17)
before_remove
回调作为Associations callbacks中的选项存在。它与before_destroy
不同,后者是ActiveRecord callback。
这是您使用它的方式:
class Person < ActiveRecord::Base
has_many :limbs, :before_remove => :print_message
def print_message(limb)
# limb variable references to a Limb instance you're removing
# ( do some action here )
# ...
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
您还错误地调用remove
方法。
p = Person.create
l = Limb.create(:person => p)
p.limbs.first.destroy
这里你在Limb
实例上调用它,这就是没有触发任何内容的原因。
在您创建的关联上调用它:
p = Person.create
l = Limb.create(:person => p)
p.limbs.destroy(l)
修改强>
为了保留最少的相关对象,您可以执行以下操作:
class Person < ActiveRecord::Base
has_many :limbs, :before_remove => :preserve_mimimum
def preserve_minimum(limb)
raise "Minimum must be preserved" if limbs.count == 1
end
end
class Limb < ActiveRecord::Base
belongs_to :person
end
然而,这不会在p.limbs.destroy_all
上触发,因此你必须做这样的事情p.limbs.each {|l| p.limbs.destroy(l)}
为什么它不会被destroy_all
触发?
因此:
def destroy_all(conditions = nil)
find(:all, :conditions => conditions).each { |object| object.destroy }
end
它迭代关联的每个元素并对对象执行销毁操作而不是关联,这就是原因。
答案 1 :(得分:1)
将before_remove
替换为before_destroy
。
编辑 - 处理最少肢数
class Limb < ActiveRecord::Base
belongs_to :creature
before_destroy :count_limbs
def count_limbs
return false if self.creature.limbs.length <= self.creature.min_limbs
end
end
我认为,那种回归是假的,会阻止它被摧毁。虽然我错了
答案 2 :(得分:0)
我不能说我之前曾使用before_remove
回调,但不确定它是否存在。
之前的destroy回调应该是在Limb模型上,而应该是这样的:
class Limb < ActiveRecord::Base
belongs_to :person
before_destroy :print_message
def print_message
puts 'removing a limb'
end
end