Ruby on Rails双模型嵌套表单出现Undefined 'map' for nilClass错误求助
问题原因及修复方案
1. 验证失败时@ingredients为nil导致报错
当菜单保存失败执行render :new时,不会重新执行new动作的代码,此时@ingredients未被赋值,collection_select调用@ingredients.map时就会触发undefined method 'map' for nil:NilClass错误。
修改控制器create动作:
def create @menu = Menu.new permit_params @ingredients = Ingredient.all # 新增该行,确保验证失败时@ingredients存在 if @menu.save redirect_to @menu else render :new, status: :unprocessable_entity end end
2. 解决MenusIngredient模型的验证冲突
模型中同时设置validates_presence_of :menu_id和belongs_to :menu, optional: true,两者矛盾:optional: true允许menu_id为空,但验证又强制要求非空,会导致嵌套关联对象无法随菜单一起创建(菜单未保存时menu_id为nil)。
修改MenusIngredient模型:
class MenusIngredient < ApplicationRecord # 移除validates_presence_of :menu_id,由belongs_to关联逻辑处理 validates_presence_of :ingredient_id validates_presence_of :qty belongs_to :menu, optional: true belongs_to :ingredient end
3. 优化嵌套字段的渲染逻辑
原表单用硬循环0..9,但fields_for只会渲染已构建的关联对象,导致大部分循环块为空。应在控制器中预先构建足够的关联对象,让fields_for自动渲染。
修改控制器new动作:
def new @menu = Menu.new 10.times { @menu.menus_ingredients.build } # 预先构建10个关联对象 @ingredients = Ingredient.all end
修改表单代码:
移除硬循环,直接用fields_for遍历所有已构建的关联对象:
<h1> New Menu </h1> <%= form_with model: @menu do |form| %> <div> <%= form.label :name %> <%= form.text_field :name %> </div> <%= form.fields_for :menus_ingredients do |mi| %> <div> <%= mi.label :ingredient_id, "食材" %> <%= mi.collection_select :ingredient_id, @ingredients, :id, :name, prompt: "选择食材" %> <%= mi.text_field :qty, placeholder: "单位:克" %> </div> <% end %> <div> <%= form.submit %> </div> <% end %>
4. 优化参数强转(可选但推荐)
原代码用permit!允许所有参数存在安全风险,建议明确指定允许的参数:
def permit_params params.require(:menu).permit( :name, menus_ingredients_attributes: [:id, :ingredient_id, :qty, :_destroy] # 加上:_destroy可支持后续删除嵌套对象(按需添加) ) end
内容的提问来源于stack exchange,提问作者Muhammad Prasasta
相关产品推荐
相关产品推荐

