HABTM - 唯一性约束

时间:2011-02-14 03:38:14

标签: ruby-on-rails has-and-belongs-to-many unique-constraint

我有两个具有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关联中的连接关系。

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

4 个答案:

答案 0 :(得分:37)

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

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

请参阅In a join table, what's the best workaround for Rails' absence of a composite key?

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

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 ::回滚是在内部捕获但未重新加载。

修改

请勿使用我正在添加自定义验证的部分。它有点有用但有更好的选择。

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

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

(此代码段来自Rails Guides v.3)。阅读Rails Guides v 3.2.13寻找4.4.2.19:uniq

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:5)

我更喜欢

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

其他选项参考here

答案 3 :(得分:5)

在Rails 5中,您需要使用distinct代替uniq

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

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