具有树节点属性的Rails树模型

时间:2019-04-10 12:55:03

标签: ruby-on-rails database activerecord

我需要在Rails(5.2)中为具有以下要求的树结构创建一个模型:

  • 树叶可以在多个树结构中重复使用
  • 树节点也可以具有属性(例如示例中的数量) 下面)

我决定使用以下数据库模型:

db model

创建这些表的迁移看起来像这样:

class CreateItems < ActiveRecord::Migration[5.2]
  def change
    create_table :items, id: false, primary_key: :item_no do |t|
      t.string :item_no, null: false
      t.string :item_name
      t.float :inventory

      t.timestamps
    end
    add_index :items, :item_no, :unique => true
    execute "ALTER TABLE items ADD PRIMARY KEY (item_no);"
  end
end

class CreateTrees < ActiveRecord::Migration[5.2]
  def change
    create_table :trees, id: false, primary_key: %i[parent_item_no child_item_no] do |t|
      t.string :parent_item_no
      t.string :child_item_no
      t.float :quantity

      t.timestamps
    end
    execute "ALTER TABLE trees ADD PRIMARY KEY (parent_item_no, child_item_no);"
  end
end

我的问题是设置Rails模型以使用关联反映此数据模型。

class Tree < ApplicationRecord
  self.primary_keys = :parent_item_no, :child_item_no
  belongs_to :item_no, class_name: 'Item', foreign_key: :parent_item_no
  has_many :item_no, class_name: 'Item', foreign_key: :child_item_no
end

父关系还可以,但是has_many显然无法正常工作,因为它期望Item表中有外键。 我需要一种访问树节点的子代的方法。

你会怎么做? 我感谢任何建议。

1 个答案:

答案 0 :(得分:0)

我相信您会做类似的事情:

class Tree < ApplicationRecord
  belongs_to :parent_item, class_name: 'Item', foreign_key: :parent_item_no
  belongs_to :child_item,  class_name: 'Item', foreign_key: :child_item_no
end

然后,您需要执行以下操作:

class Item < ApplicationRecord
  has_many :children_trees, class_name: 'Tree', foreign_key: :parent_item_no
  has_many :child_items, through: :children_trees
  has_many :parent_trees, class_name: 'Tree', foreign_key: :child_item_no
  has_many :parent_items, through: :parent_trees
end

由于未经测试,您可能需要对此付诸实践。

然后,您将可以执行以下操作:

@item.child_items
@item.parent_items

由于您规定Item(您称其为“树叶”,但也可以称为“节点”)可以在多个树结构中重复使用,因此您需要做一些花哨的工作以确保您在特定树的上下文中检索父项和子项。但这是另一个问题。

我个人不知道这是关于什么的:

self.primary_keys = :parent_item_no, :child_item_no

如果您要确保在两个给定的Tree之间只有一个Item实例,那么我建议您validate uniqueness using a scope

我不知道您为什么使用_no而不是_id。如果您有parent_item_idchild_item_id而不是parent_item_nochild_item_no,那么我认为您可以:

class Tree < ApplicationRecord
  belongs_to :parent_item, class_name: 'Item'
  belongs_to :child_item,  class_name: 'Item'
end

但是,也许您有充分的理由违背惯例。

顺便说一句,Tree似乎是班上一个奇怪的名字。 Tree是层次结构中所有边缘和节点的集合。但是,您使用Tree来表示结构的边缘。这对我来说似乎很奇怪。但是,嘿,这是个人喜好问题。因此,如果它使您的船漂浮起来,那就去吧!