You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails多模型树形结构建模失败求助:基于Closure Tree实现层级关系

问题描述

需要构建如下层级的树形数据结构:

Cohort
  |_____RecruitmentPhase
           |_______________Source

但因涉及多个带不同数据成员的模型,尝试用Closure Tree建模失败。现有模型类与数据库表结构如下:

a) RecruitmentPhase 模型与表结构

模型代码

class RecruitmentPhase < ApplicationRecord
  belongs_to :cohort, optional: true
  belongs_to :parent, class_name: 'Cohort', required: false , dependent: :destroy
  has_many :disease_phaseid_questionnaires, dependent: :destroy, foreign_key: :phase_id
  def phase_name_ret
    phase_name
  end
end

表结构SQL

create_table "recruitment_phases", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
  t.integer "phase_id"
  t.string "phase_token"
  t.string "phase_name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.integer "parent_id"
end

b) Cohort 模型与表结构

模型代码

class Cohort < ApplicationRecord
end

表结构SQL

create_table "cohorts", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
  t.string "disease_code_id"
  t.string "disease_name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.bigint "patient_id"
  t.integer "recruitment_phase_ids"
  t.index ["patient_id"], name: "index_cohorts_on_patient_id"
end

c) Source 模型与表结构

模型代码

class Source < ApplicationRecord
  belongs_to :recruitment_phase, class_name: 'recruitment_phase', foreign_key: 'recruitment_phase_id'
end

表结构SQL

create_table "sources", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", force: :cascade do |t|
  t.integer "source_id"
  t.string "source_token"
  t.string "source_name"
  t.string "description"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

解决方案

你当前的问题核心是误用了Closure Tree的适用场景,Closure Tree用于同一模型内的树形结构(如分类层级),而你要的是跨模型的一对多层级关联,用普通Active Record关联即可实现。具体修改步骤如下:

1. 修正模型关联

Cohort 模型

添加与RecruitmentPhase的一对多关联:

class Cohort < ApplicationRecord
  has_many :recruitment_phases, dependent: :destroy
end

RecruitmentPhase 模型

移除错误的parent关联(关联到Cohort是错误的),添加与Source的一对多关联,并修正belongs_to :cohort的可选性:

class RecruitmentPhase < ApplicationRecord
  belongs_to :cohort, null: false # 确保每个招募阶段都属于一个队列
  has_many :sources, dependent: :destroy
  has_many :disease_phaseid_questionnaires, dependent: :destroy, foreign_key: :phase_id

  def phase_name_ret
    phase_name
  end
end

Source 模型

修正class_name的大小写错误(Rails模型类名首字母大写),并移除冗余的外键声明(默认外键就是recruitment_phase_id):

class Source < ApplicationRecord
  belongs_to :recruitment_phase, null: false
end

2. 修正数据库表结构

需要创建三个迁移来调整表结构:

迁移1:修复RecruitmentPhases表

添加cohort_id外键,移除冗余的phase_id和错误的parent_id:

class FixRecruitmentPhasesTable < ActiveRecord::Migration[7.0]
  def change
    # 添加cohort外键
    add_reference :recruitment_phases, :cohort, null: false, foreign_key: true
    # 移除不需要的字段
    remove_column :recruitment_phases, :parent_id
    remove_column :recruitment_phases, :phase_id
  end
end

迁移2:修复Sources表

添加recruitment_phase_id外键,移除冗余的source_id:

class FixSourcesTable < ActiveRecord::Migration[7.0]
  def change
    # 添加recruitment_phase外键
    add_reference :sources, :recruitment_phase, null: false, foreign_key: true
    # 移除冗余主键字段
    remove_column :sources, :source_id
  end
end

迁移3:修复Cohorts表

移除冗余的recruitment_phase_ids字段(一对多关联不需要存储ID数组):

class RemoveRecruitmentPhaseIdsFromCohorts < ActiveRecord::Migration[7.0]
  def change
    remove_column :cohorts, :recruitment_phase_ids
  end
end

3. 验证层级结构

修改完成后,即可通过以下方式访问层级数据:

  • 获取某个队列下的所有招募阶段:cohort.recruitment_phases
  • 获取某个招募阶段下的所有来源:recruitment_phase.sources
  • 链式访问完整层级:cohort.recruitment_phases.each { |rp| rp.sources }

内容的提问来源于stack exchange,提问作者raghu palakodety

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 23:35:22