ruby on rails - rails - Creando un modelo que tiene una estructura de árbol.
ruby on rails tutorial (5)
Artículo completo: https://blog.francium.tech/best-practices-for-handling-hierarchical-data-structure-in-ruby-on-rails-b5830c5ea64d
Una mesa sencilla
Table Emp
id: Integer
name: String
parent_id: Integer
Asociaciones
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
Definicion del alcance
class Emp < ApplicationRecord
----
----
scope :roots, -> { where(parent_id: nil) }
end
Recuperacion de datos
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
Eager Loading
def tree_data
output = []
Emp.roots.includes(subs: {subs: {subs: subs}}}.each do |emp|
output << data(emp)
end
output.to_json
end
Tengo categorías que están en una estructura de árbol. Estoy tratando de vincularlos definiendo un padre para cada uno. (No pude averiguar cómo llamar al parent
la propiedad parent
por lo que es solo una category
por ahora, pero significa el padre).
class Category < ActiveRecord::Base
has_one :category # the parent category
end
Pero la relación termina al revés.
La función getter está en la categoría secundaria (correctamente) pero la category_id
se almacena en la principal:
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" (!!)
El padre debe poder tener varios hijos para que esto no funcione.
Debes echar un vistazo a la gema de la ascendencia: Ancestry
Proporciona toda la funcionalidad que necesita y es capaz de obtener a todos los descendientes, hermanos, padres, etc. con una única consulta SQL mediante el uso de una variante de rutas materializadas para que tenga un mejor rendimiento que las respuestas de auto-unión y act_as_tree anteriores.
La categoría debe tener muchas categorías, y la clave externa de cada categoría debe ser el parent_id
. Entonces, cuando haces parent.children
, enumera todas las categorías que tienen parent_id=parent.id
.
¿Has leído sobre la herencia de una sola mesa?
Lo que estás buscando es unirte a ti mismo. Consulte esta sección de la guía de Rails en: 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
Cada categoría belong_to
un padre, incluso sus categorías padre. Puede crear una categoría principal a la que pertenecen todas las categorías de nivel más alto, luego puede ignorar esa información en su aplicación.
Puede utilizar la gema act_as_tree para lograr esto, encuentre el ejemplo y el enlace a continuación.
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