我有两个具有HABTM关系的模型 - 用户和角色。
我想在连接(users_roles表)中添加唯一性约束,该约束表示user_id和role_id必须是唯一的。在Rails中,看起来像:
validates_uniqueness_of :user, :scope => [:role]
当然,在Rails中,我们通常没有一个模型来表示HABTM关联中的连接关系。
所以我的问题是添加约束的最佳位置在哪里?
答案 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)
答案 3 :(得分:5)
在Rails 5中,您需要使用distinct
代替uniq
另外,请尝试这样做以确保唯一性
has_and_belongs_to_many :foos, -> { distinct } do
def << (value)
super value rescue ActiveRecord::RecordNotUnique
end
end