建模相同模型之间的两种不同关系

时间:2015-06-28 19:51:18

标签: ruby-on-rails ruby ruby-on-rails-4 model associations

两种模式组织和用户具有1:多关系,其中组织有多个用户(成员;用户也可以与任何组织关联):

class Organization < ActiveRecord::Base
  has_many :users, dependent: :destroy
  accepts_nested_attributes_for :users, :reject_if => :all_blank, :allow_destroy => true
  validates_associated :users
end

class User < ActiveRecord::Base
  belongs_to :organization, inverse_of: :users
end

一切正常,各种测试都通过了 现在我为主持人功能添加了一个额外的关系,其中用户可以拥有(多个)组织的主持人权限。因此,通过第三个模型建立了很多:很多关系,我将其命名为主持人:

class Organization < ActiveRecord::Base
  has_many :users, dependent: :destroy
  accepts_nested_attributes_for :users, :reject_if => :all_blank, :allow_destroy => true
  has_many :moderators, class_name: "Moderator", foreign_key: "reviewee_id", dependent: :destroy
  has_many :users, through: :moderators, source: :reviewer
  validates_associated :users
end

class User < ActiveRecord::Base
  belongs_to :organization, inverse_of: :users
  has_many :moderators, class_name:  "Moderator",   foreign_key: "reviewer_id", dependent: :destroy
  has_many :organizations, through: :moderators, source: :reviewee
end

class Moderator < ActiveRecord::Base
  belongs_to :reviewee, class_name: "Organization"
  belongs_to :reviewer, class_name: "User"
end

我故意使用reviewerreviewee名称。如果我只是在主持人模型中使用user_idorganization_id,我可能会搞砸了。因为如果您要引用@user.organization,那么就不能定义要使用哪种关系。它会使用用户和组织之间的1:多关系,还是许多关系:很多通过关系......?通过对许多人使用不同的名称:很多通过关系,@user.organization应该引用1:many关系,而@user.reviewee例如应该引用许多:很多通过关系。

然而,在实施之后,突然间各种测试都失败了。例如:我有一个表单,为组织注册一个额外的用户。 Clicing a按钮将organization_id传递给要为其创建其他用户的表单。现在突然这个ID没有被传递到表单,我得到了所有nil错误,因为组织没有被定义(即使链接仍然是例如url/member?organization_id=43)。我可以提供更多的例子。

因为新关系似乎存在某种冲突。也许它无法理解何时使用许多:很多通过关系和1:多关系时,即使我使用了差异审阅者和审稿人姓名...... 我是否错误地建模或者不可能有两个两个相同模型之间的不同关系?

如果我从组织模型中删除第二条has_many :users行,则所有测试都会再次通过。所以问题似乎是我有两次这种关系。

1 个答案:

答案 0 :(得分:1)

处理此问题的一个好的和常见的模式称为资源范围角色。

用户可以拥有许多角色(父亲,母亲,主持人,草裙舞等),在某些情况下,角色的范围限定为特定资源。就像父亲/母亲的范围是用户(孩子)或主持人可以限定论坛。

拥有像super-admin这样没有作用于资源的“系统级”角色也很常见。

class User < ActiveRecord::Base
  has_many :roles
  scope :moderators, ->{ joins(:roles).where( roles: { name: 'moderator' } ) }
  belongs_to :organization
end

# columns: name:string, resource_id:int, resource_type:string, user_id:int
class Role < ActiveRecord::Base
  belongs_to :user
  belongs_to :resource, polymorphic: true
end

class Organization < ActiveRecord::Base
  has_many :roles, as: :resource
  has_many :users
  # This is just a relationship to users with a scope
  has_many :moderators, -> { moderators }, class_name: 'User'
end

所以要添加主持人,我们会这样做:

organization = Organization.find(1)
organization.roles.create(user: organization.users.find(1), name: 'moderator')

获取组织的所有版主:

moderators = Organization.find(1).moderators

这里真棒的是我们可以在任何资源上使用我们的Role类 - 而不仅仅是组织。更好的是,有很多宝石可以提供这种功能,例如Months