Rails是否有可能拥有一个可以包含两种类型模型的模型:多个模型的树



本质上,我希望具有以下结构:

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

最新更新