HABTM - 唯一性约束

ker*_*lin 17 ruby-on-rails unique-constraint has-and-belongs-to-many

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

  • user - has_and_belongs_to_many:roles
  • role - belongs_to:user

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

validates_uniqueness_of :user, :scope => [:role]
Run Code Online (Sandbox Code Playgroud)

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

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

Art*_*rov 37

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

add_index :users_roles, [ :user_id, :role_id ], :unique => true, :name => 'by_user_and_role'
Run Code Online (Sandbox Code Playgroud)

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

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

class User < ActiveRecord::Base
has_and_belongs_to_many :roles, :before_add => :validates_role
Run Code Online (Sandbox Code Playgroud)

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

def validates_role(role)
  raise ActiveRecord::Rollback if self.roles.include? role
end
Run Code Online (Sandbox Code Playgroud)

ActiveRecord :: Rollback是内部捕获但未重新加载.

编辑

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

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

class Parts < ActiveRecord::Base
  has_and_belongs_to_many :assemblies, :uniq => true, :read_only => true
end  
Run Code Online (Sandbox Code Playgroud)

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

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

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


Spy*_*ros 5

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

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


ken*_*ken 5

我更喜欢

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles, -> { uniq }
end
Run Code Online (Sandbox Code Playgroud)

其他选项在这里参考

  • 使用` - > {uniq}`有助于只显示其中一个重复记录,但仍然会发生重复. (2认同)

Cal*_*leb 5

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

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

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

  • 有效,但看起来不太漂亮,是吗? (2认同)
  • +1 用于自动救援独特的错误。不需要重复的 HABTM 来防止保存其他属性 (2认同)
  • 这是唯一在 Rails 6 中有效的解决方案 (2认同)
  • 唯一适用于 Rails 7 的解决方案 (2认同)