如何设置has_many,通过:仅使用唯一的组合

问题描述 投票:0回答:1

我正在尝试设置 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

现在,由于我正在构建的表单中出现错误,我创建了重复的关系。当然,我可以尝试通过不在表单中出现错误来防止这种情况,但我想知道是否有一种方法可以强制仅使用用户和实体的唯一组合?

ruby-on-rails has-many-through
1个回答
0
投票

是的。

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
© www.soinside.com 2019 - 2024. All rights reserved.