求教,通过父类找出所有子类的问题。
有张类别表 types,字段如下: id name type_id 内容: 1 汽车 0 2 红色汽车 1
就这样汽车是红色汽车的父类,我建立模型 type: class Type < ActiveRecord::Base has_many :subordinates, class_name: "Type", foreign_key: "type_id"
belongs_to :type, class_name: "Type" end 测试: a = Type.find 1 a.type 这样无法找出红色汽车
但是 a = Type.find 2 a.type 这样能找出红色汽车父类
想问下,该如何配置呢?我有父类 ID,想查找出所有子类,谢谢。
如果在乎读的效率要用先根遍历: http://bbs.neten.de/viewthread.php?tid=813
ruby 代码 https://github.com/collectiveidea/awesome_nested_set
@Peter 用了 awesome_nested_set
但是执行很多方法报: NoMethodError: undefined method `quoted_table_name' for ActiveRecord::AttributeMethods::Serialization::Type:Class
我用的 rails4 对 rails4 支持不好?
目前版本的 awesome_nested_set 确实对 rails4 支持不好。 戳这里: http://ruby-china.org/topics/15943
可以用 预排序遍历树算法 :)
自己实现的,有些地方不怎么样,轻喷
class Category < ActiveRecord::Base
include Concerns::RecursionTree
# 先序遍历, 拿到 [depth, name]
# 注意:由于 Rails 将不存在的列名过滤,此处只能使用 lft 来存储深度!!
scope :get_sorted_array, -> do
Category
.joins('AS node, categories AS parent')
.select('node.id, node.name, node.slug, node.model_id, (COUNT(parent.slug)) - 1 AS lft')
.where('node.lft BETWEEN parent.lft AND parent.rgt')
.group('node.parent_id, node.slug')
.order('node.lft')
end
end
#
# 预排序遍历树算法(modified preorder tree traversal algorithm)
# 来存储无限分级的树结构,以改进查询的时空复杂度。render 需要实现 get_sorted_array
#
module Concerns
module RecursionTree
extend ActiveSupport::Concern
included do
attr_accessor :children, :depth
end
# 同级插入
def place_with(id)
klass = self.class
if new_record?
klass.transaction do
brother = klass.select(:lft, :rgt, :parent_id).find(id)
raise AlertError.new('不能用虚拟的根节点作为兄弟节点') if brother.lft == 0
parent, right = brother.parent_id, brother.rgt
klass.where('rgt > ?', right).update_all('rgt = rgt + 2')
klass.where('lft > ?', right).update_all('lft = lft + 2')
self.parent_id = parent
self.lft = right + 1
self.rgt = right + 2
self.save!
end
end
end
# 节点追加
def append_to(id)
klass = self.class
if new_record?
klass.transaction do
left = klass.select(:lft).find(id).lft
klass.where('rgt > ?', left).update_all('rgt = rgt + 2')
klass.where('lft > ?', left).update_all('lft = lft + 2')
self.parent_id = id
self.lft = left + 1
self.rgt = left + 2
self.save!
end
end
end
# 删除节点
def remove
klass = self.class
klass.transaction do
left, right = self.lft, self.rgt
distance = (right - left - 1) / 2
klass.where('lft BETWEEN ? AND ?', left, right).delete_all
klass.where('rgt > ?', right).update_all(['rgt = rgt - ?', distance])
klass.where('lft > ?', right).update_all(['lft = lft - ?', distance])
end
end
module ClassMethods
# 采用迭代和栈替代递归,避免递归带来的内存问题
def render
@nodes = get_sorted_array
root = @nodes.shift # 出队无意义的根节点
current_depth = -1
item_path = [root]
@nodes.each do |item|
depth = item.depth = item.lft # 留意 get_sorted_array 方法的注释
if depth > current_depth
(item_path[-1].children ||= []) << item
item_path << item
elsif depth == current_depth
item_path[-1] = item
item_path[-2].children << item
else
until item_path.pop.lft == depth; end
item_path[-1].children << item
item_path << item
end
current_depth = depth
end
root
end
end
end
end