我正在尝试这样做
has_many :roles, :before_add => :enforce_unique
def enforce_unique(assoc)
false if exists? assoc
end
从文档:“如果before_add回调引发异常,则该对象不会被添加到集合中”。上面的使用false不会阻止添加,所以我不得不这样做:
def enforce_unique(assoc)
raise if exists? assoc
end
这样,它确实没有被添加,但它也引发了必须处理的异常。这里对我来说不是很有用。我更喜欢这种行为更像常规AR回调before_save,其中返回FALSE也会阻止保存(或添加),但不会引发异常。
在上面的这种情况下,我希望这只是不要静默添加关联。有没有办法做到这一点?我错过了什么?或者在这里提出例外是唯一的选择吗?
答案 0 :(得分:2)
解决这个问题的方法我认为是使用throw
和catch
,它们在Ruby中用于流量控制。提出异常并不合适,因为这不是特殊情况。
我最终做了:
catch(:duplicate) do
association.create({})
end
然后在before_add
回调中,我做了:
if(Class.where({}).first)
throw :duplicate
end
更多关于throw / catch的信息:
http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/
答案 1 :(得分:1)
如果关联不是多态的,您可以执行以下操作:
validates_uniqueness_of :name_of_model
在角色里面,name_of_model我们与你联系的内容
答案 2 :(得分:1)
def enforce_unique |obj, x|
v = obj.roles
if i = v.index(x)
v.slice! i
end
end
答案 3 :(得分:0)
由于这个问题是关于保存而不是阻止它暂时包含在列表中(例如,通过对控制其模型不感兴趣的控制器),您可以尝试覆盖相关模型中的保存,如果角色不能保存它存在:
class Role < ActiveRecord::Base
belongs_to :user, inverse_of: :roles
def save
super unless self.new_record? && user.has_existing_role?(self)
end
end
旁注:当与Active Record模式一起使用时,我不会购买瘦控制器参数,因为必须将业务逻辑放在某处。对于像Active Record这样的业务领域差的模式(特别是没有提到Ruby AR gem),它确实需要在上面存在一层(即在控制器层中),你可以使用服务对象或装饰模式作为实现这一点的手段
另一种方法是覆盖关联等更新方法<<
,如果匹配现有角色,则静默删除角色。有关覆盖关联方法的详细信息,请参见ActiveRecord Association Class Methods Documentation