【问题标题】:Is it possible for Rails to have a Model that can contain two types of models: Tree of multiple modelsRails 是否有可能拥有一个可以包含两种模型的模型:多个模型的树
【发布时间】:2012-10-17 18:48:33
【问题描述】:

基本上我希望有以下结构:

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)

我通过尝试以下 gem 进行了研究:acts_as_tree、ancestry、nested_sets 和closure_tree。

他们能够保持一棵树,这是我面临的挑战的一半;但是他们无法将另一个模型添加到树结构中,因为他们总是期望孩子们是同一个模型。 [例如:ActiveRecord::AssociationTypeMismatch: Group (#2179078840) 预期,从closure_tree gem 中获取 Person(#2171128160)]

是否有可能让上面定义的结构与树中的两个不同模型一起工作?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 rails-activerecord


    【解决方案1】:

    您可以定义一个用于“树节点”的模型。每个节点可以有一个布尔字段来指示它是否是叶子,另一个字段(或多个字段)指向该节点“持有”的组/个人。 (如果您只使用一个字段来指向 Group/Person,那么您可以使用多态关联,或者您可以编写一个自定义 SQL 查询,根据 "groups" 或 "people" 的值进行检索叶”字段。)

    【讨论】:

      【解决方案2】:

      本质上,您希望 Person 从 Group 继承。我不知道为什么你会想要这种结构,因为组和人不是同一类型的东西。

      也许您想详细说明您尝试共享的功能。在两个非常不同的对象之间共享功能的更“红宝石”方式是通过模块,而不是通过继承。

      【讨论】:

        【解决方案3】:

        我会使用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
        

        【讨论】:

          【解决方案4】:

          您是否需要 Tree 附带的功能。如果没有,你可以做一些简单的事情......

          在每个对象上放置一个 group_id。一个 nil 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
          

          【讨论】:

            【解决方案5】:

            closure_treetests 中有使用 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"))
            …
            

            【讨论】:

              【解决方案6】:

              完全正常工作 这可以改进,但我只做了你想做的事


              gem 'ancestry'
              

              group.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
              

              person.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
              

              【讨论】:

                猜你喜欢
                • 2013-12-16
                • 2018-08-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-12-26
                相关资源
                最近更新 更多