如何与两种用户类型和一种模型与具有和属于轨道中的许多关系进行关联?

Posted

技术标签:

【中文标题】如何与两种用户类型和一种模型与具有和属于轨道中的许多关系进行关联?【英文标题】:How to do associations with two user types and one model with has and belongs to many relationships in rails? 【发布时间】:2016-02-02 13:52:07 【问题描述】:

我已经尝试解决这个问题一段时间了。看了here,但这不是我需要的。

我有三个模型:User、Group、GroupMembership。用户可以是教师和学生,因此用户通过 UserRoles 表具有不同的角色。小组可以有多名教师。我想要的是这样的:

Role.rb
has_and_belongs_to_many :users  #this works fine

User.rb
has_and_belongs_to_many :roles  # this works fine

has_many :group_memberships
has_many :groups, through: :group_memberships
has_many :teachers, through: :group_memberships
has_many :students, through: :group_memberships

Group.rb
has_many :students, through: :group_memberships
has_many :teachers, through: :group_memberships

GroupMembership.rb
belongs_to :student
belongs_to :teacher
belongs_to :group

具有角色的用户工作正常,用户的不同角色没有问题。问题在于组成员身份。以上只是我想要做的事情,但在某些情况下,我需要提供源或类名,我不确定具体要做什么。我应该创建什么样的迁移?

【问题讨论】:

你怎么知道用户是老师还是学生?有没有设置用户类型的字段? @AnezioCampos 是的,有一个表 Roles 和一个连接表 UserRoles @Dan 将type 字段添加到User,并从User 继承两个给学生和老师。 @МалъСкрылевъ 他正在使用角色来区分不同类型的用户。恕我直言,这是比 STI 更好的选择,因为 STI 无法加入。 @RichPeck 因为 STI 依赖于数据库中关于要加载哪些类以及它们有哪些关系的信息,因此很难创建有效的连接 VS 正常情况。并非不可能 - 但让一切都变得非常有趣。它与使用多态关系时几乎相同。 【参考方案1】:

如果需要,我会删除它;我很确定你会从使用 source: 在你的 UserGroup 模型中受益:

#app/models/user.rb
class User < ActiveRecord::Base
   has_and_belongs_to_many :roles  # this works fine

   has_many :group_memberships
   has_many :groups,   through: :group_memberships

   has_many :teachers, through: :group_memberships, source: :teacher
   has_many :students, through: :group_memberships, source: :student
end

#app/models/group_membership.rb
class GroupMembership < ActiveRecord::Base
  belongs_to :student, class_name: "User"
  belongs_to :teacher, class_name: "User"

  belongs_to :group
end

#app/models/group.rb
class Group < ActiveRecord::Base
   ...
   has_many :students, through: :group_memberships, source: :student
   has_many :teachers, through: :group_memberships, source: :teacher
end

这未经测试,我对source 没有超级经验,所以它可能是错误的。

【讨论】:

您在正确的轨道上 - 但是在 GroupMembership 连接表上使用两个不同的列并不是一个很好的解决方案。 当然;我会留下它,直到 OP 看起来。感谢您的反馈!【参考方案2】:

建模有点偏离 - GroupMembership 应该充当多对多连接模型。这意味着每个用户和组都需要一个 GroupMembership 来将它们联系在一起。

您还可以将用户和角色之间的关系更改为has_many through:,以便您可以在联接模型中拥有元数据。

class User
  has_many :user_roles
  has_many :roles, through: :user_roles
  has_many :group_memberships
  has_many :groups, through: :group_memberships
end

class Role
  has_many :user_roles
  has_many :users, through: :user_roles
end

class UserRole
  belongs_to :user
  belongs_to :role
end

class Group
  has_many :group_memberships
  has_many :users, through: :group_memberships
end

class GroupMembership
  belongs_to :user
  belongs_to :group
end

要查询您现在可以使用的具有特定角色的组用户:

teachers = group.users.where(users:  roles: [Role.find_by(name: 'teacher')])

为了方便一点,我们可以设置特殊的条件关系:

class Group
  has_many :group_memberships
  has_many :users, through: :group_memberships
  has_many :teachers, -> where(users:  roles: [Role.find_by(name: 'teacher')] )  through: :group_memberships, source: :user
end

请注意,我们需要指定source,因为它告诉AR“老师”实际上是group_memberships.user

【讨论】:

首先感谢您的帮助。如果某个组的学生也将担任老师的角色,那么您的解决方案就会出现问题。所以我需要区分组中的用户在什么时候是学生,什么时候是老师。 您可能想看看 Rolify 以及他们如何解决它 Rolify 在角色和角色所应用的资源之间使用多态关系。因此,在您的情况下,您可以将 group_id 添加到 users_roles 或使关系多态,以便将角色应用于不同的资源。

以上是关于如何与两种用户类型和一种模型与具有和属于轨道中的许多关系进行关联?的主要内容,如果未能解决你的问题,请参考以下文章

ELK日志报警插件ElastAlert

在核心数据中搜索所有具有空“对多”关系的对象

虚方法与两种重写方法的比较

如何实现 has_many :through 关联两种方式?

如何在 laravel 中建立用户与用户的关系? [关闭]

如何根据用户在 Laravel 5 中的角色选择用户?