设置和测试以防止ActiveRecord多对多关系中的重复

设置和测试以防止ActiveRecord多对多关系中的重复,activerecord,ruby-on-rails-4,rspec,Activerecord,Ruby On Rails 4,Rspec,我有以下关于“项目”和“颜色”之间的多对多关系的课程 和“项目”不应该有重复的“颜色”, 例如:- 如果“项目1”有“蓝色”和“红色”,则我们不能在“项目1”中添加另一个“红色” 这是正确的设置方法吗 class Item < ActiveRecord::Base has_many :item_colors has_many :colors, :through => item_colors end class Color < ActiveRec

我有以下关于“项目”和“颜色”之间的多对多关系的课程

和“项目”不应该有重复的“颜色”, 例如:- 如果“项目1”有“蓝色”和“红色”,则我们不能在“项目1”中添加另一个“红色”

这是正确的设置方法吗

class Item < ActiveRecord::Base  
    has_many :item_colors  
    has_many :colors, :through => item_colors  
end  

class Color < ActiveRecord::Base
    has_many :item_colors
    has_many :items, :through => item_colors
end

class ItemColor < ActiveRecord::Base
    belongs_to :item
    belongs_to :color

    validates_uniqueness_of :color, scope: :item
end

请告知。

当您添加第二种颜色时,这是因为父对象
@项
已保存,即它不是
新记录

假设它是一个
多:通过
关联,它是,这反过来会引发异常,因为您的联接模型
ItemColor
无法验证唯一性

在您的情况下,您有两种选择:

  • 救援
    异常并手动或手动管理错误消息
  • 如果您使用连接模型只是为了添加验证层,那么可以将其删除,改用连接模型并将关联作为一个集合处理,例如

    > item = FactoryGirl.create(:item)
    > color = FactoryGirl.create(:color)
    > 10.times { item.colors |= [color] } # you can add it n times...
    > item.colors.count # => 1  ...still only one is saved b/c it is a union set.
    
  • 你觉得怎么样

    更新:如果您确实想显示错误消息,您可以,例如

    if item.colors.include?(color)
      item.errors.add(:colors, "color already selected")
    else
      item.colors |= [color]
    end
    

    @斯纳克摩尔:这对你有什么帮助吗?很抱歉回复得太晚了。由于开放id问题,我有几天无法登录。我喜欢2选项,但我如何用错误消息提醒用户?我想您不需要错误消息。即使用户多次尝试添加相同的颜色,系统也总是只保存一种,因为它就像一个集合。就用户体验而言,IMHO,错误信息可以被抑制,b/c用户很可能会错误地尝试添加现有颜色。最终,他们的行动将产生预期的结果,不会出现任何错误。然而,如果您真的想警告您的用户,我已经用一个示例解决方案更新了答案。
    > item = FactoryGirl.create(:item)
    > color = FactoryGirl.create(:color)
    > 10.times { item.colors |= [color] } # you can add it n times...
    > item.colors.count # => 1  ...still only one is saved b/c it is a union set.
    
    if item.colors.include?(color)
      item.errors.add(:colors, "color already selected")
    else
      item.colors |= [color]
    end