创建具有树结构的模型

时间:2011-02-24 19:12:16

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

我有树结构的类别。我试图通过为每个人定义父母来将他们联系在一起。 (我无法弄清楚如何调用属性parent所以它现在只是category,但它意味着父级。)

class Category < ActiveRecord::Base

    has_one :category # the parent category

end 

但这种关系最终走错了路。

getter函数在子类别上(正确),但category_id存储在父类中:

parent = Category.create(:name => "parent")
child = Category.create(:name => "child", :category => parent)

parent.id # 1
child.id # 2

child.category_id # nil
parent.category_id # 2

child.category.name # "parent" (!!)

父母需要有多个孩子,所以这不会起作用。

5 个答案:

答案 0 :(得分:34)

您正在寻找的是自我加入。请查看Rails指南的这一部分:http://guides.rubyonrails.org/association_basics.html#self-joins

class Category < ActiveRecord::Base
  has_many :children, class_name: "Category", foreign_key: "parent_id"
  belongs_to :parent, class_name: "Category"
end

每个类别都会belong_to为父级,甚至是您的父类别。您可以创建最高级别类别所属的单个类别父级,然后您可以忽略应用程序中的该信息。

答案 1 :(得分:3)

您可以使用acts_as_tree gem来实现此目的,查找以下示例并链接。

https://github.com/amerine/acts_as_tree/tree/master

class Category < ActiveRecord::Base
  include ActsAsTree

  acts_as_tree order: "name"
end

root      = Category.create("name" => "root")
child1    = root.children.create("name" => "child1")
subchild1 = child1.children.create("name" => "subchild1")

root.parent   # => nil
child1.parent # => root
root.children # => [child1]
root.children.first.children.first # => subchild1

答案 2 :(得分:3)

你应该看一下祖先的宝石:https://github.com/stefankroes/ancestry

它提供了您需要的所有功能,并且能够通过使用物化路径的变体通过单个SQL查询获取所有后代,兄弟,父母等,因此它将具有比自联接和acts_as_tree答案更好的性能以上。

答案 3 :(得分:2)

类别应该有很多类别,每个类别的外键应该是parent_id。因此,当您执行parent.children时,它会列出所有具有parent_id=parent.id

的类别

您是否阅读过单表继承?

答案 4 :(得分:0)

完整文章 - https://blog.francium.tech/best-practices-for-handling-hierarchical-data-structure-in-ruby-on-rails-b5830c5ea64d

简单表

Table Emp
id: Integer
name: String
parent_id: Integer

协会

app/models/emp.rb
class Emp < ApplicationRecord
  has_many :subs, class_name: 'Emp', foreign_key: :parent_id
  belongs_to :superior, class_name: 'Emp', foreign_key: :parent_id
end

范围定义

class Emp < ApplicationRecord
  ----
  ----
  scope :roots, -> { where(parent_id: nil) }
end

获取数据

def tree_data
  output = []
  Emp.roots.each do |emp|
    output << data(emp)
  end
  output.to_json
end
def data(employee)
  subordinates = []
  unless employee.subs.blank?
    employee.subs.each do |emp|
      subordinates << data(emp)
    end
  end
  {name: employee.name, subordinates: subordinates}
end

渴望加载

def tree_data
  output = []
  Emp.roots.includes(subs: {subs: {subs: subs}}}.each do |emp|
    output << data(emp)
  end
  output.to_json
end