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

如何在创建Recipe记录时自动生成has_many through关联的RecipeSeason数据

解决has_many through关联下创建Recipe时自动生成关联记录的问题

需求说明

在创建Recipe记录的同时,自动在recipe_seasons表插入一条关联记录:

  • recipe_id使用新生成的Recipe的ID
  • season_id采用硬编码的@month值(此处固定为1)

现有代码

模型代码

Recipe模型

class Recipe < ApplicationRecord
  attribute :name
  attribute :published

  has_many :ingridients, dependent: :destroy
  has_many :instructions, dependent: :destroy
  
  has_many :recipe_seasons
  has_many :seasons, through: :recipe_seasons

  accepts_nested_attributes_for :recipe_seasons

  validates_presence_of :name
end

Season模型

class Season < ApplicationRecord
    has_many :recipe_seasons
    has_many :recipes, through: :recipe_seasons
end

RecipeSeason模型

class RecipeSeason < ApplicationRecord
  belongs_to :recipe
  belongs_to :season

  validates_presence_of :recipe
  validates_presence_of :season

  accepts_nested_attributes_for :season
end

控制器代码

def new
  @month = 1
  @recipe = Recipe.new
  @recipe.recipe_seasons.build(season_id: @month).build_recipe
end

def create
  @recipe = Recipe.new(recipe_params)
  @recipe.save
  redirect_to recipes_path
  flash[:notice] = I18n.t("recipe.created")
end

private

def recipe_params
  params.require(:recipe)
    .permit(:name, :published, recipe_seasons_attributes:[:recipe_id, :season_id ])
end

表单代码

<%= form_with(model: @recipe) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name, required: true %>
  <%= f.label :published %>
  <%= f.check_box :published, class: "form-control", placeholder: "Tick if done" %>
  <%= f.submit %>
<% end %>
<%=link_to  t("back"), recipes_path %>

解决方案

提供两种实现方式,按需选择:

方式一:手动创建关联记录(更直接)

这种方式无需修改表单,直接在Recipe保存后创建关联:

  1. 调整控制器new方法:
def new
  @month = 1
  @recipe = Recipe.new
end
  1. 修改控制器create方法:
def create
  @month = 1
  @recipe = Recipe.new(recipe_params)
  
  if @recipe.save
    # 自动创建关联记录
    @recipe.recipe_seasons.create(season_id: @month)
    flash[:notice] = I18n.t("recipe.created")
    redirect_to recipes_path
  else
    # 处理保存失败的情况,渲染表单页面
    render :new
  end
end
  1. 简化参数白名单:
private
def recipe_params
  params.require(:recipe).permit(:name, :published)
end

方式二:使用嵌套属性(通过表单传递)

这种方式利用Rails嵌套属性特性,通过隐藏字段传递season_id:

  1. 调整控制器new方法:
def new
  @month = 1
  @recipe = Recipe.new
  # 预构建关联,用于生成表单字段
  @recipe.recipe_seasons.build(season_id: @month)
end
  1. 修改表单,添加嵌套隐藏字段:
<%= form_with(model: @recipe) do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name, required: true %>
  <%= f.label :published %>
  <%= f.check_box :published, class: "form-control", placeholder: "Tick if done" %>
  
  <!-- 添加隐藏字段传递season_id -->
  <%= f.fields_for :recipe_seasons do |rs_form| %>
    <%= rs_form.hidden_field :season_id, value: @month %>
  <% end %>
  
  <%= f.submit %>
<% end %>
<%= link_to t("back"), recipes_path %>
  1. 调整参数白名单:
private
def recipe_params
  params.require(:recipe)
    .permit(:name, :published, recipe_seasons_attributes: [:season_id])
end

无需允许recipe_id,嵌套属性会自动将新创建的Recipe ID赋值给关联记录


模型注意事项

  • Recipe模型已正确配置has_many :recipe_seasons和accepts_nested_attributes_for :recipe_seasons,无需修改
  • RecipeSeason模型的validates_presence_of :recipe和:season会自动满足:通过关联创建时recipe_id自动填充,season_id由硬编码提供

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 19:35:17