我正在尝试设置 2 个模型(实体、用户),其中每个用户可以使用多个实体,每个实体可以有多个用户使用它们。我希望关系是唯一的,即用户不能与同一实体工作两次,并且实体不能两次拥有同一用户。
我正在使用 has_many through 关系,并且我在“through”类中存储额外的信息,例如访问权限。
class Entity < ApplicationRecord
has_many :user_entity_roles, dependent: :destroy
accepts_nested_attributes_for :user_entity_roles
has_many :users, through: :user_entity_roles, dependent: :restrict_with_error
end
class User < ApplicationRecord
has_many :user_entity_roles, dependent: :destroy
has_many :entities, through: :user_entity_roles, dependent: :restrict_with_error
end
class UserEntityRole < ApplicationRecord
belongs_to :entity, touch: true
belongs_to :user
end
现在,由于我正在构建的表单中出现错误,我创建了重复的关系。当然,我可以尝试通过不在表单中出现错误来防止这种情况,但我想知道是否有一种方法可以强制仅使用用户和实体的唯一组合?
在
entity_id
和 user_id
上添加唯一索引将在数据库级别强制唯一性:
class AddUniqueCompoundIndexToUserEntityRole < ActiveRecord::Migration[7.0]
def change
add_index(:user_entity_roles, [:entity_id, :user_id], unique: true)
end
end
请注意,在修复重复数据之前,您的数据库不会让您添加此索引 - 例如通过删除重复的角色。
索引将导致数据库拒绝任何重复数据,这将导致数据库驱动程序错误。为了防止该错误并提供更好的用户反馈,您需要应用程序级别验证:
class UserEntityRole < ApplicationRecord
belongs_to :entity, touch: true
belongs_to :user
validates_uniqueness_of :entity_id, scope: :user_id
end
验证将捕获 99% 的潜在重复项,但不能保证其本身的唯一性,因为它容易出现竞争条件。
validates_associated
:
class Entity < ApplicationRecord
has_many :user_entity_roles, dependent: :destroy
accepts_nested_attributes_for :user_entity_roles
has_many :users, through: :user_entity_roles, dependent: :restrict_with_error
validates_associated :user_entity_roles
end