默默地跳过添加与before_add关联回调而不是引发异常?

时间:2011-09-01 17:02:27

标签: ruby-on-rails activerecord callback

我正在尝试这样做

 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也会阻止保存(或添加),但不会引发异常。

在上面的这种情况下,我希望这只是不要静默添加关联。有没有办法做到这一点?我错过了什么?或者在这里提出例外是唯一的选择吗?

4 个答案:

答案 0 :(得分:2)

解决这个问题的方法我认为是使用throwcatch,它们在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