【问题标题】:Rails preventing duplicates in polymorphic has_many :through associationsRails 防止多态 has_many 中的重复:通过关联
【发布时间】:2011-02-22 16:28:11
【问题描述】:

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

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

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

s = Story.find_by_id(1)

t = Tag.find_by_id(1)

s.tags << t

s.tags << t

我的 taggings 连接表将添加两个条目,每个条目具有完全相同的数据(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: Validation failed 异常。似乎当您将关联添加到列表时,它会调用保存! (而不是保存 sans !)方法,如果出现问题而不是仅仅返回 false,则会引发异常。这不是我想要发生的。我想我可以用 try/catch 包围任何添加新标签的尝试,但这违背了你不应该期待你的异常的想法,这是我完全期望发生的事情。

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

【问题讨论】:

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


    【解决方案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 过滤器来确保标签列表没有任何重复项。这会产生更多的开销,因为它会在每次保存时发生。

    【讨论】:

    • 这正是我想要的。但是,我不确定您将如何重载 tags。我真的很想重载
    • 哦是的..我忘了,你必须定义一个标签方法,它返回一个具有&lt;&lt;方法的对象。但是, self.tags self.tags += new_tags。答案已更新。
    【解决方案2】:

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

    :uniq

    如果为 true,将从集合中省略重复项。与 :through 结合使用很有用。

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

    【讨论】:

    • uniq 选项实际上并不能阻止将任何内容放入数据库。事实上,根据我的测试,它看起来只有在实例化一个对象时才起作用,因为它似乎调用了 uniq!在对象的关联列表上,此时它只是在内存中删除它们。如果添加了任何重复的关联,它们仍会显示在对象列表和数据库中。这是一个完美的例子,说明为什么 Rails 文档很糟糕并且迫切需要澄清和/或修订。
    【解决方案3】:

    我相信这行得通...

    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
    

    如果您遇到任何错误或其他问题,请告诉我:]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-16
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-01
      相关资源
      最近更新 更多