Rails 5嵌套表单问题:assignment内嵌volunteer_shift配置及报错排查
报错核心原因
你遇到的undefined method 'build' for nil:NilClass错误由两个问题叠加导致:
- Assignment模型中仅声明了
attr_accessor :volunteer_event虚拟属性,没有配置和VolunteerEvent的ActiveRecord关联,虚拟属性默认值为nil,调用build方法自然报错 - 你当前需要嵌套的是VolunteerShift,不需要在Assignment的new动作中操作VolunteerEvent对象
1. 模型关联修正
修改Assignment模型代码,删除多余的虚拟属性,可选补充跨关联的volunteer_event声明:
class Assignment < ApplicationRecord # 删除这行多余的虚拟属性声明 # attr_accessor :volunteer_event belongs_to :volunteer_shift, optional: true # 新增时还未赋值,加optional避免验证提前报错 has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type # 可选补充:如果需要直接从Assignment访问关联的VolunteerEvent,可以加下面这行 has_one :volunteer_event, through: :volunteer_shift belongs_to :contact ,optional: true validates_presence_of :volunteer_shift validates_associated :volunteer_shift accepts_nested_attributes_for :volunteer_shift, allow_destroy: true # ...其余代码保持不变 end
2. 控制器逻辑修正
new动作修改
替换错误的build代码,使用belongs_to关联专用的构建方法:
def new @assignment = Assignment.new # 把原来的@assignment.volunteer_event.build改成下面这行 @assignment.build_volunteer_shift @my_url = {:action => "create", :id => params[:id]} end
补充强参数配置
在assignments_controller的strong参数中放行嵌套属性,避免提交时参数被过滤:
def assignment_params params.require(:assignment).permit( # 你原来允许的其他字段... volunteer_shift_attributes: [:id, :volunteer_task_type_id, :roster_id, :program_id, :set_description, :set_date, :_destroy] ) end
3. 嵌套表单修正
你当前局部模板里用的select_tag生成的字段名称不符合嵌套属性的参数结构,会导致参数无法正确传递,同时要修正重复id的HTML规范问题:
<div class="name large flex-row"> <%= vs.label :volunteer_shift %> </div> <!-- 删除重复的id="volunteer_shift",同一页面id不能重复 --> <div class="d-flex flex-row volunteer_shift_wrap"> <div class="col-sm-12 p-2"> <div class="text-right"> <div class="field"> <%= vs.label :volunteer_task_type_id %> <!-- 把select_tag改成表单生成器的select方法,自动生成正确的嵌套字段名 --> <%= vs.select :volunteer_task_type_id, options_from_collection_for_select([VolunteerTaskType.new(:description => ""), VolunteerTaskType.instantiables.effective_on(Date.today)].flatten, "id", "description") %> </div> <div class="field"> <%= vs.label :roster_id %> <%= vs.select :roster_id, options_from_collection_for_select([Roster.new(:name => ""), Roster.all].flatten, "id", "name") %> </div> <div class="field"> <%= vs.label :program_id %> <%= vs.select :program_id, options_from_collection_for_select([Program.new(:name => ""), Program.where(:volunteer => true)].flatten, "id", "name")%> </div> <div class="field"> <%= vs.label :set_description %> <%= vs.text_field :set_description %> </div> <div class="field"> <%= vs.label :set_date, "Date" %> <%= vs.text_field :set_date %> </div> </div> </div> </div>
内容的提问来源于stack exchange,提问作者thefonso
相关产品推荐
相关产品推荐

