某些背景信息:
一家公司有很多用户。谁是主人。
除了所有者之外,您可以销毁公司中的所有人。
摧毁主人,就像摧毁公司一样。所以你必须company.destroy
如果他是所有者,我会阻止销毁用户,除非你正在摧毁公司。
我的代码:
class Company < ActiveRecord::Base
before_destroy { @bool_allow_owner_be_destroyed = true ; p object_id }
belongs_to :owner, class_name: 'User', foreign_key: 'owner_id'
has_many :employees, class_name: 'User', dependent: :destroy
def can_owner_be_destroyed?
p @bool_allow_owner_be_destroyed
p object_id
!!@bool_allow_owner_be_destroyed
end
end
class User < ActiveRecord::Base
belongs_to :company
before_destroy :cancel_if_owner
def cancel_if_owner
!self.is_owner? || self.company.can_owner_be_destroyed?
end
end
我的问题:
当我致电company.destroy
时,我看到了我的调试。我正在传递before_destroy { @bool_allow_owner_be_destroyed = true ; p object_id }
,我看到了我的object_id。
当我传递can_owner_be_destroyed?
方法时,我有相同的object_id,但我的@bool_allow_owner_be_destroyed
是nil
。它是我的代码中唯一触及@bool_allow_owner_be_destroyed
变量的地方。
有什么想法吗?
答案 0 :(得分:0)
根据声明的顺序,在为父对象运行before_destroy
回调之前,可以销毁您的依赖对象。
您可以自己测试一下:
class Company < ActiveRecord::Base
belongs_to :owner, class_name: 'User', foreign_key: 'owner_id'
has_many :employees, class_name: 'User', dependent: :destroy
before_destroy do
@test_variable = "hi"
puts @test_variable
puts "Company destroyed"
end
end
class User < ActiveRecord::Base
belongs_to :company
before_destroy do
puts company.instance_variable_get(:@test_variable)
puts "User destroyed"
end
end
所有"Company destroyed"
已经消失后, users
将是最后打印的内容,并且每个用户都会得到一个空字符串puts
来代替“hi”,因为@test_variable
尚未确定。但是,如果您在声明关联之前声明回调,则会先打印"Company destroyed"
,并且您应该看到"hi"
打印为每个User
为destroy
。
如果你问我,那是非常意外的。回调可能使事情过于复杂并且经常被过度使用,我们怎样才能保证我们确切地知道在这样复杂的执行流程中发生了什么? Ruby有一个内置的后代对象回调系统,它更容易理解,并且在行为上可以预测:super
class Company
def destroy(*args, &block)
@bool_allow_owner_be_destroyed = true
super
end
end
在调用super
之前执行的任何操作都将保证在任何依赖对象被销毁之前运行或由destroy
触发的任何其他对象。使用super
时没有歧义。
要取消destroy
的{{1}},您还可以使用owner
以及早期super
。
return
总的来说,我建议尝试使用实例变量在ActiveRecord对象上携带状态可能会让您在某些时候遇到麻烦,特别是如果关联对象依赖于该状态或者您的应用程序运行的多个实例同一时间。
您可以将其设为db字段并保留更改。
class User < ActiveRecord::Base
belongs_to :company
def destroy(*args, &block)
return unless can_be_destroyed?
super
end
def can_be_destroyed?
!is_owner? || company.can_owner_be_destroyed?
end
end