【问题标题】:Activerecord has_and_belongs_to_many with nested groupsActiverecord has_and_belongs_to_many 与嵌套组
【发布时间】:2019-02-05 23:27:04
【问题描述】:

我需要将用户组存储在其他组中,有点像 Windows Active Directory。

我有以下有效的方法

ActiveRecord::Schema.define do
  create_table :users do |table|
    table.column :name, :string
  end
  create_table :groups do |table|
    table.column :name, :string
  end
  create_join_table :users, :groups do |t|
  end
end

class User < ActiveRecord::Base
  has_and_belongs_to_many :groups
end

class Group < ActiveRecord::Base
  has_and_belongs_to_many :users
  # has_and_belongs_to_many :groups
end

peter  = User.create(id: 1, Name: 'Peter')
thomas = User.create(id: 2, Name: 'Thomas')
inf = Group.create(id: 1, Name: 'Informatics')

peter.groups << inf
inf.users << thomas
p User.find_by(Name: 'Peter').groups
p Group.find_by(Name: 'Informatics').users

但我也想做以下事情

sm = Group.create(id: 2, Name: 'System')
inf.groups << sm

最简单的方法是什么?

【问题讨论】:

  • 您需要查看自指关联。一些例子是herehere
  • 你有什么理由使用 has_and_belongs_to 而不是 has_many => 吗?后者可能更灵活一些。
  • @BKSpureon 组可以有很多用户和组,用户可以有很多组,这可能与 has_many_through 吗?为什么更灵活?你有例子吗?
  • 您无需(实际上任何)相应成本即可保持灵活性。如果您想在连接表/连接模型中添加额外的字段怎么办?如果您希望将来在您的连接模型中包含回调和验证逻辑怎么办?您可以免费保留额外的灵活性。
  • @peter 这里是讨论此问题的文档的链接:guides.rubyonrails.org/…

标签: ruby activerecord orm relational-database self-referencing-table


【解决方案1】:

您可以通过在Group 模型中使用自引用关系来完成此操作。这是一个表中的记录可能指向同一个表中的其他记录的地方。

基本上,主组的parent_id 将是NULL,子组的parent_id 将设置为其父组的列ID。

组模型:

has_many :users

has_many :sub_groups, class_name: "Group", foreign_key: :parent_id
has_many :sub_group_users, through: :sub_groups, source: :users 
belongs_to :parent, class_name: 'Group', foreign_key: :parent_id, optional: true
# This is a scope to load the top level groups and eager-load their users, sub-groups, and the sub-groups' users too.
scope :top_level, -> { where(parent_id: nil).includes :users, sub_groups: :users}

组控制器:

def show
  @group            = Group.find(params[:id])
  @category         = @group.parent
  @users            = @group.users
  @sub_group        = @group.sub_groups.first
  unless @sub_group.nil?
    @relatives      = @group.sub_group_users
  end
end

private

  def cat_params
    params.require(:group).permit(:name, :parent_id, :sub_group)
  end


  def main_group
    @group = Group.parent_id.nil?
  end

在您的groups 表中,添加此列:t.integer "parent_id"

在您的users 表中,添加此列:t.integer "group_id"

您还需要将:group_id 添加到您的user_params 中(在您的用户控制器中)。

用户模型: belongs_to :group

在您的组显示视图中:

<% if @category %>
  <% @users.each do |user| %>

  <% end %>
<% else %>
  <% @relatives&.each do |user| %>

  <% end %>
<% end %>

【讨论】:

  • 谢谢杰克,这是一个开始,我已经尝试在单独的答案中使用它,请随时修改它或您的。我只会接受您或其他人的解决方案,而不是我自己的尝试
  • @peter 哦,对不起,我在想你在哪里用 Rails 框架运行 Ruby。我还应该补充一点,我认为我的解决方案仅适用于显示属于子组的用户。它们可以属于一个主要组,但我认为我在尝试显示它时遇到了问题。
【解决方案2】:

我将我的 attemtp 与 Jake 的解决方案放在一个单独的答案中,以使其与原始解决方案分开,并使 cmets 和仅对该解决方案进行编辑成为可能。 我不打算接受这个作为答案! 此外,我将整个脚本放在这里,以便人们可以立即进行测试。 该解决方案只使用了 2 个表,而不是原来的 3 个,这很好,但还不完整,请参阅代码末尾的我的注释。

require 'active_record'
require 'logger'

ActiveRecord::Base.establish_connection(
:adapter => "sqlite3",
:database  => ":memory:"
)

ActiveRecord::Schema.define do
  create_table :users do |table|
    table.column :name, :string
    table.column :group_id, :integer
  end
  create_table :groups do |table|
    table.column :name, :string
    table.column :user_id, :integer
    table.column :parent_id, :integer
  end
end

class User < ActiveRecord::Base
  belongs_to :group
  has_many :groups
end

class Group < ActiveRecord::Base
  has_many :users
  has_many :sub_groups, class_name: "Group", foreign_key: :parent_id
  has_many :sub_group_users, through: :sub_groups, source: :users 
  belongs_to :parent, class_name: 'Group', foreign_key: :parent_id, optional: true
  # This is a scope to load the top level groups and eager-load their users, sub-groups, and the sub-groups' users too.
  scope :top_level, -> { where(parent_id: nil).includes :users, sub_groups: :users}
end

peter  = User.create(id: 1, name: 'Peter')
thomas = User.create(id: 2, name: 'Thomas')
erika  = User.create(id: 3, name: 'Erika')
inf    = Group.create(id: 1, name: 'Informatics')
devs   = Group.create(id: 2, name: 'Devs')
log    = Group.create(id: 3, name: 'Logistics')

# peter.groups << inf # doesn't work
devs.users << thomas
devs.users << peter
inf.users << erika

# expect inf.groups << devs to work
inf.sub_groups << devs

# doesn't work
p peter.groups #<ActiveRecord::Associations::CollectionProxy []>

# only gives users added straight to the group
p inf.users #<ActiveRecord::Associations::CollectionProxy [#<User id: 3, name: "Erika", group_id: 1>]>

# this should be added to inf.users
p inf.sub_group_users 
#<ActiveRecord::Associations::CollectionProxy [#<User id: 1, name: "Peter", group_id: 2>, #<User id: 2, name: "Thomas", group_id: 2>]>

# works
p Group.top_level
#<ActiveRecord::Relation [#<Group id: 1, name: "Informatics", user_id: nil, parent_id: nil>]>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多