2

是否有一種簡單或至少優雅的方式來防止多態has_many通過關聯重複項?Rails預防重複多態has_many:通過關聯

我有兩個模型,故事和可以標記的鏈接。我正在有意識地決定不要在這裏使用插件。我想真正理解所發生的一切,而不是依賴於別人的代碼,我沒有完全掌握。

看看我的問題是在得到,如果我跑在控制檯下(假設的故事和標籤對象已經存在於數據庫中)

s = Story.find_by_id(1) 

t = Tag.find_by_id(1) 

s.tags << t 

s.tags << t 

我引用的Tagging加入將增加表中的兩個條目到它,每個具有相同的確切數據(tag_id = 1,taggable_id = 1,taggable_type =「故事」)。這對我來說似乎不太合適。因此,在試圖防止這種情況發生,我添加以下到我的標籤模型:

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添加新標籤的嘗試,但是這與你不應該期待你的例外並且這是我期望發生的事情相反。

有沒有更好的方法來做到這一點,當我想要做的只是默默無聞地將對象保存到數據庫因爲重複存在而不會引發異常?

回答

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過濾器,以確保標籤的列表中沒有任何重複。這會導致更多的開銷,因爲它會在每次保存時發生。

+0

這幾乎就是我想要的。不過,我不確定如何去重載標籤<<方法。我試着複製add_tags方法,並將其重命名爲「tags <<」,但每當我嘗試調用它時,它都會給我帶來NoMethodError:未定義的方法'標籤',用於#。我真的很喜歡<<方法被重載,因爲使用它似乎更自然。 – seaneshbaugh 2010-05-22 20:26:11

+0

噢,我忘了,你必須定義一個標籤方法,它返回一個具有'<<'方法的對象。但是,self.tags << new_tags無論如何都不起作用,因爲它只會嘗試在標籤集合中添加一組標籤作爲元素。你會想爲該行使用'self.tags + = new_tags'。答案已更新。 – 2010-05-23 07:10:23

1

定義has_many關係時,可以設置uniq選項。 Rails的API文檔說:

:uniq

If true, duplicates will be omitted from the collection. Useful in conjunction with :through.

(摘自: 「支持選項」 副標題http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001833

+0

uniq選項實際上並不能阻止任何東西放入數據庫。實際上,基於我的測試,它看起來只是在一個對象被實例化時才起作用,因爲它似乎調用了uniq!在該對象的關聯列表上,該關聯列表在那一刻只是在內存中刪除它們。如果添加了任何重複的關聯,它們仍然顯示在對象的列表和數據庫中。這是一個很好的例子,說明爲什麼rails文檔很糟糕,並且急需澄清和/或修訂。 – seaneshbaugh 2010-05-22 12:43:11

+3

':uniq'與防止數據庫中的重複項無關... – 2010-05-22 14:47:36

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 

讓你得到任何錯誤,我知道或者與之相關的東西]: