Rails防止多态的重复has_many:通过关联

时间:2010-05-22 10:53:54

标签: ruby-on-rails ruby activerecord polymorphic-associations has-many-through

是否有一种简单或至少优雅的方法来防止多态has_many中的重复条目通过关联?

我有两个可以标记的模型,故事和链接。我有意识地决定不在这里使用插件。我想要真正了解正在发生的一切,而不是依赖于我没有完全掌握的其他人的代码。

要查看我的问题是什么,如果我在控制台中运行以下内容(假设数据库中已存在故事和标记对象)

s = Story.find_by_id(1)

t = Tag.find_by_id(1)

s.tags << t

s.tags << t

我的标记连接表将添加两个条目,每个条目具有相同的确切数据(tag_id = 1,taggable_id = 1,taggable_type =“Story”)。这对我来说似乎不太合适。因此,为了防止这种情况发生,我在标记模型中添加了以下内容:

before_validation :validate_uniqueness

def validate_uniqueness
    taggings = Tagging.find(:all, :conditions => { :tag_id => self.tag_id, :taggable_id => self.taggable_id, :taggable_type => self.taggable_type })

    if !taggings.empty?
        return false
    end

    return true
end

它几乎按预期工作,但如果我尝试向故事或链接添加重复标记,我会得到一个ActiveRecord :: RecordInvalid:验证失败异常。似乎当你向列表添加一个关联时,它会调用save! (而不是保存sans!)方法,如果出现问题而不仅仅返回false,则会引发异常。这不是我想要发生的事情。我想我可以围绕任何使用try / catch添加新标签的尝试,但这违背了你不应该期待你的异常的想法,这是我完全期望发生的事情。

是否有一种更好的方法可以在我想要做的只是默默地不将对象保存到数据库时因为存在重复而引发异常?

3 个答案:

答案 0 :(得分:1)

您可以在定义has_many关系时设置uniq选项。 Rails API文档说:

  

:uniq的

     

如果为true,将从集合中省略重复项。与以下内容结合使用:通过。

(摘自:http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001833在“支持的选项”小标题下

答案 1 :(得分:1)

你可以通过几种方式做到这一点。

定义一个自定义 add_tags方法,加载所有现有标记,然后检查并仅添加新标记。

示例:

def add_tags *new_tags
  new_tags = new_tags.first if tags[0].kind_of? Enumerable #deal with Array as first argument
  new_tags.delete_if do |new_tag|
    self.tags.any? {|tag| tag.name == new_tag.name}
  end
  self.tags += new_tags
end

您还可以使用before_save过滤器来确保标记列表没有任何重复项。这会产生更多的开销,因为它会在每次保存时发生。

答案 2 :(得分:0)

我相信这有效......

class Tagging < ActiveRecord::Base
   validate :validate_uniqueness 

   def validate_uniqueness
      taggings = Tagging.find(:all, :conditions => {
         :tag_id => self.tag_id,
         :taggable_id => self.taggable_id,
         :taggable_type => self.taggable_type }) 

      errors.add_to_base("Your error message") unless taggings.empty? 
   end 
end

如果您收到任何错误或其他内容,请告知我们:]