我将我的 attemtp 与 Jake 的解决方案放在一个单独的答案中,以使其与原始解决方案分开,并单独对此解决方案进行评论和编辑。我不打算接受这个作为答案!此外,我将整个脚本放在这里,以便人们可以立即进行测试。该解决方案仅使用 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>]>