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

时间:2014-09-12 02:29:08

标签: activerecord ruby-on-rails-4 rspec

对于“Item”和“Color”之间的多对多关系,我有以下类。

“项目”不应该有重复的“颜色”, 例如:- 如果“Item1”具有“Blue”& “红色”然后我们不能将另一个“红色”添加到“Item1”

这是设置它的正确方法吗?

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

我对重复颜色的测试。它是如何测试的吗?

describe "item should not have duplicated colors" do
    before do
        @item = FactoryGirl.create(:item)
        @color1 = FactoryGirl.create(:color)
        @item.colors << @color1
        @item.colors << @color1
        @item.save
    end

    it { should_not be_valid }
end

当我在rails控制台中尝试此操作时,当我向项目添加重复的颜色时,它将失败 但是我没有在item.errors.message中收到错误消息,而是获得了ActiveRecord异常

"ActiveRecord::RecordInvalid: Validation failed: Color has already been taken"

请告知。

1 个答案:

答案 0 :(得分:3)

添加第二种颜色时,它是automatically saved,因为父对象@item已经保存,即它不是new_record

鉴于它是一个has_many :through关联,它是always saved with the bang version of save!,这反过来会引发异常,因为您的连接模型ItemColor无法验证唯一性。

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

  1. rescue例外并手动管理错误消息;
  2. 如果你只是为了添加验证层而使用连接模型,你可以摆脱它,改为使用HABTM并将关联作为一个集来处理,例如。

    > 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.
    
  3. 听起来怎么样?

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

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