本质上,我希望具有以下结构:
class Group < ActiveRecord::Base
end
class Person < ActiveRecord::Base
end
Group_1 (parent)
Group_2 (child)
Person_1 (grandchild)
Person_2 (grandchild)
Person_3 (child)
Group_3 (child)
Person_4 (grandchild)
Group_4 (grandchild)
Person_5 (great grandchild)
我通过尝试以下宝石进行了研究:acts_as_tree、血统、nested_sets和closure_tree。
他们能够保留一棵树,这是我所面临的挑战的一半;但他们无法在树结构中添加另一个模型,因为他们总是期望孩子们是同一个模型。[例如:活动记录::关联类型不匹配:组 (#2179078840) 预期,从 closure_tree gem 获得人员 (#2171128160) ]
是否有可能让上面定义的结构在树内使用两个不同的模型?
您可以定义一个将用于"树节点"的模型。每个节点可以有一个布尔字段,指示它是否是叶子,另一个字段(或多个字段)指向该节点"持有"的组/人员。(如果仅使用一个字段指向组/人员,则可以使用多态关联,也可以编写自定义 SQL 查询,该查询根据"叶"字段的值从"组"或"人员"检索。
本质上,您希望 Person 从 Group 继承。我不知道你为什么要这种结构,因为群体和个人不是同一类型的东西。
也许您想详细说明您尝试共享的功能。在两个非常不同的对象之间共享功能的更"ruby"的方式是通过模块,而不是通过继承。
我会使用 habtm
方法解决这个问题。但是,在构建数据库表时,您可能需要小心。 Rails 代码将如下所示
class Group < ActiveRecord::Base
has_and_belongs_to_many :people, class: Person
end
class Person < ActiveRecord::Base
has_and_belongs_to_many :groups
end
您是否需要树附带的功能。如果没有,你可以做一些简单的事情...
在每个对象上放置一个group_id。 零group_id意味着它属于根。
class Group < ActiveRecord::Base
belongs_to :group # The parent
has_many :groups
has_many :people
end
class Person < ActiveRecord::Base
belongs_to :group
end
closure_tree测试中有一些使用 STI 或单表继承的示例,这是您想要使用的。但是,请阅读 STI - "组"似乎与"组"具有相同类型的字段或行为,因此这可能不是正确的解决方案。
但是,假设是,请创建一个数据库迁移,将字符串类型列添加到模型中。我会将表命名为通用名称,例如"节点"。
class AddTypeToNode < ActiveRecord::Migration
def change
add_column :nodes, :type, :string
end
end
然后创建一个基类和子类:
class Node < ActiveRecord::Base
abstract_class = true
acts_as_tree
end
class Group < Node
end
class Person < Node
end
然后,您将能够执行以下操作:
g1 = Group.create(:name => "Group_1")
g2 = g1.add_child(Group.create(:name => "Group_2"))
p1 = g2.add_child(Person.create(:name => "Person_1"))
p2 = g2.add_child(Person.create(:name => "Person_2"))
…
完全工作这可以改进,但我只做了你想做的
<小时 />gem 'ancestry'
组.rb
<小时 />class Group < ActiveRecord::Base
attr_accessible :parent_id, :name
has_many :people
attr_accessor :relationship
has_ancestry
def people_group_children
(people + children).sort_by(&:created_at)
end
RELATIONSHIP = ['parent', 'child', 'grandchild']
def relationship_by_depth(depth_diff)
@relationship = RELATIONSHIP[depth_diff] || (depth_diff-2) * 'great ' + RELATIONSHIP[2]
end
def arrange_as_desired(ancestor_depth = 0, parent_depth = 0)
output = '<ul>'.html_safe
output.safe_concat name + '(' + relationship_by_depth(depth - ancestor_depth) + ')'
people_group_children.map do |child|
output.safe_concat '<li>'
output.safe_concat child.arrange_as_desired(ancestor_depth, depth)
output.safe_concat '</li>'
end
output.safe_concat '</ul>'
output
end
end
人.rb
<小时 />class Person < ActiveRecord::Base
attr_accessible :group_id, :name
belongs_to :group
attr_accessor :relationship, :depth
def parent; group; end
def people_group_children; []; end
RELATIONSHIP = ['parent', 'child', 'grandchild']
def relationship_by_depth(depth_diff)
@relationship = RELATIONSHIP[depth_diff] || 'great ' * (depth_diff-2) + RELATIONSHIP[2]
end
def arrange_as_desired(ancestor_depth = 0, parent_depth = 0)
output = '<ul>'.html_safe
@depth = parent_depth + 1
output.safe_concat name + "(#{relationship_by_depth(depth - ancestor_depth)})"
people_group_children.map do |child|
output.safe_concat '<li>'
output.safe_concat child.arrange_as_desired(ancestor_depth, depth)
output.safe_concat '</li>'
end
output.safe_concat '</ul>'
output
end
end
groups\show.html.erb
<小时 /><ul>
<%= raw @group.arrange_as_desired %>
</ul>
<小时 /><小时 />迁移
create_groups.rb
<小时 />class CreateGroups < ActiveRecord::Migration
def change
create_table :groups do |t|
t.string :name
t.string :ancestry
t.timestamps
end
end
end
create_people.rb
<小时 />class CreatePeople < ActiveRecord::Migration
def change
create_table :people do |t|
t.integer :group_id
t.string :name
t.timestamps
end
end
end