2012-01-08 86 views
1

我在Rails應用程序中與連接表有許多關係。我在模型中使用has_many:通過習慣用法。爲了簡單起見,我們可以打電話給我的第一班學生,我的第二堂課以及連接表班級註冊(包含字段student_id和course_id)。我想確保給定的學生最多與給定課程關聯一次(即{student_id,course_id}元組在註冊表中應該是唯一的)。多對多唯一性約束測試不起作用

所以我有一個強制執行此唯一性的遷移。

def change 
    add_index :enrollments, [:student_id, :course_id], :unique => true 
end 

另外我的模型類的定義是這樣的:

class Student < ActiveRecord::Base 
    has_many :enrollments 
    has_many :courses, :through => :enrollment 

end 

class Course < ActiveRecord::Base 
    has_many :enrollments 
    has_many :students, :through => :enrollment 

end 

class Enrollment < ActiveRecord::Base 
    belongs_to :student 
    belongs_to :course 

    validates :student, :presence => true 
    validates :course,  :presence => true 
    validates :student_id, :uniqueness => {:scope => :course_id} 

end 

在Rails控制檯,我可以做到以下幾點:

student = Student.first 
course = Course.first 
student.courses << course 
#... succeeds 
student.courses << course 
#... appropriately fails and raises an ActiveRecord::RecordInvalid exception 

在我的RSpec的測試,我做的完全一樣的東西,我也不例外,下面的代碼:

@student.courses << @course 
expect { @student.courses << @course }.to raise_error(ActiveRecord::RecordInvalid) 

所以我的測試失敗和報告:

expected ActiveRecord::RecordInvalid but nothing was raised 

這是怎麼回事嗎?我可能做錯了什麼?我如何解決它?

回答

0

有幾件事情在這裏,可能會發生:

  1. @courses已經使用之間變化。
  2. @student在兩次使用之間發生了變化。

通過使用let您可以保護這些值免於預期之間的變化。

let(:course) { Course.first } 
let(:student) { Student.first } 
subject{ student.courses << course << course } 
it { should raise_error(ActiveRecord::RecordInvalid) } 

或者,也可能僅僅是一些錯誤代碼:)

1

Rails使用模型級別的驗證,如果你想爲uniquiness你需要使用數據庫級別嚴格的檢查 - 例如外鍵。但在這種情況下,您需要從數據庫連接器捕獲異常。 這很奇怪,因爲在我的代碼(非常類似於你的)驗證唯一引發異常。