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

如何设置has_many polymorphic self-referential关联满足菜谱组件配置需求

关联关系配置方案

我们通过新增多态中间表的方式同时满足两类需求,具体配置如下:

1. 生成中间表迁移文件

# db/migrate/[timestamp]_create_recipe_components.rb
class CreateRecipeComponents < ActiveRecord::Migration[7.0]
  def change
    create_table :recipe_components do |t|
      t.references :recipe, null: false, foreign_key: true # 归属的主菜谱ID
      t.references :componentable, polymorphic: true, null: false # 多态关联字段,指向食材/子菜谱

      t.timestamps
    end
    # 新增唯一索引,避免同一菜谱重复添加同一个组件
    add_index :recipe_components, [:recipe_id, :componentable_type, :componentable_id], unique: true, name: 'index_recipe_components_unique'
  end
end

2. 模型关联配置

  • Ingredient模型配置
# app/models/ingredient.rb
class Ingredient < ApplicationRecord
  # 反向关联:该食材被哪些菜谱引用
  has_many :recipe_components, as: :componentable, dependent: :destroy
  has_many :used_in_recipes, through: :recipe_components, source: :recipe
end
  • Recipe模型配置
# app/models/recipe.rb
class Recipe < ApplicationRecord
  # 菜谱归属菜谱书的关联可自行补充,此处省略
  belongs_to :cookbook

  # 核心多态关联:当前菜谱的所有组件(包含食材、子菜谱)
  has_many :recipe_components, dependent: :destroy
  has_many :ingredient_components, through: :recipe_components, source: :componentable, source_type: 'Ingredient'
  has_many :sub_recipe_components, through: :recipe_components, source: :componentable, source_type: 'Recipe'

  # 反向关联:当前菜谱作为子菜谱被哪些父菜谱引用
  has_many :as_sub_recipe_entries, class_name: 'RecipeComponent', as: :componentable, dependent: :destroy
  has_many :used_in_recipes, through: :as_sub_recipe_entries, source: :recipe
end
核心能力实现

1. 查询菜谱是否被其他菜谱引用

在Recipe类中添加判断方法即可:

def referenced_by_others?
  used_in_recipes.exists?
end

2. 统计菜谱的全部食材(递归包含子菜谱食材)

在Recipe类中添加递归查询方法,同时内置循环引用防护逻辑:

# 入参visited_recipes用于避免循环引用(比如A菜谱引用B,B又引用A的场景)
def all_ingredients(visited_recipes = Set.new)
  return [] if visited_recipes.include?(self.id)
  visited_recipes.add(self.id)

  # 直接关联的食材 + 所有子菜谱的全量食材
  ingredient_components + sub_recipe_components.flat_map { |sub_recipe| sub_recipe.all_ingredients(visited_recipes) }
end

使用时直接调用your_recipe.all_ingredients即可拿到包含所有子菜谱食材的全量清单。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 08:06:03