HABTM – 唯一性约束

我有两个具有HABTM关系的模型 – 用户和角色。

  • user – has_and_belongs_to_many:roles
  • role – belongs_to:user

我想在连接(users_roles表)中添加一个唯一性约束,表示user_id和role_id必须是唯一的。 在Rails中,看起来像:

validates_uniqueness_of :user, :scope => [:role] 

当然,在Rails中,我们通常没有一个模型来表示HABTM关联中的连接关系。

所以我的问题是添加约束的最佳位置在哪里?

您可以为连接表添加唯一性

 add_index :users_roles, [ :user_id, :role_id ], :unique => true, :name => 'by_user_and_role' 

在连接表中,Rails缺少复合键的最佳解决方法是什么?

您的数据库将引发exception,您必须处理该exception。
我不知道在这种情况下是否准备好使用railsvalidation,但您可以像这样添加自己的validation:

 class User < ActiveRecord::Base has_and_belongs_to_many :roles, :before_add => :validates_role 

我会默默地删除数据库调用并报告成功。

 def validates_role(role) raise ActiveRecord::Rollback if self.roles.include? role end 

ActiveRecord :: Rollback在内部捕获但未重新加载。

编辑

不要使用我正在添加自定义validation的部分 。 它有点工作,但有更好的选择。

使用:uniq关联时的:uniq选项,@ Spyros在另一个答案中建议:

 class Parts < ActiveRecord::Base has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true end 

(此代码段来自Rails Guides v.3)。 阅读Rails指南v 3.2.13,了解4.4.2.19:uniq

Rails指南v.4特别警告不要使用include? 由于可能的竞争条件而检查唯一性。

关于为连接表添加索引的部分保留。

我认为使用:uniq => true会确保您没有重复的对象。 但是,如果要在将第二个写入数据库之前检查是否存在重复项,我可能会使用find_or_create_by_name_and_description(…)。

(当然,名称和描述是您的列值)

我更喜欢

 class User < ActiveRecord::Base has_and_belongs_to_many :roles, -> { uniq } end 

其他选项在这里参考

在Rails 5中,您将要使用distinct而不是uniq

另外,请尝试这样做以确保唯一性

 has_and_belongs_to_many :foos, -> { distinct } do def << (value) super value rescue ActiveRecord::RecordNotUnique end end