我在Rails应用程序中与连接表有多对多的关系。我在我的模型中使用has_many:through idiom。为了简单起见,我们可以调用我的第一堂课程,第二堂课程和连接表课程注册(包含字段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)
Rails使用模型级验证,如果要严格检查是否需要使用数据库级别 - 例如外键。但在这种情况下,您需要从数据库连接器中捕获异常。 这很奇怪,因为在我的代码(非常类似于你)中验证了唯一的加注异常。
答案 1 :(得分:0)
这里可能会发生一些事情:
使用let
,您可以保护这些值不受期望值的影响。
let(:course) { Course.first }
let(:student) { Student.first }
subject{ student.courses << course << course }
it { should raise_error(ActiveRecord::RecordInvalid) }
或者,您的代码可能有问题:)