我有一个has many :bands, through: :event_bands
的事件模型。 band
模型validates :name, presence: true, uniqueness: true
。
在创建事件时,用户从多选中选择乐队,如下所示:
<p>
<%= f.label t(:band_playing) %>
<%= f.collection_select :band_ids, Band.order(:name), :id, :name, {}, {multiple: true} %>
</p>
但是,如果用户没有找到合适的乐队,我希望他们能够在我的活动模型中使用accepts_nested_attributes_for :bands
在同一表单上创建乐队。
所以,我把它添加到我的表单中,我用一些jQuery隐藏/显示:
<div id="new_bands">
<p>
<%= f.fields_for :bands do |ff| %>
<%= ff.label "New Band Name" %>
<%= ff.text_field :name, class: "form-control half" %>
<% end %>
</p>
</div>
但是,要使fields_for
正常工作,我需要像这样构建关联我的事件控制器:
def new
@event = current_user.events.build
1.times {@event.bands.build}
end
如果用户需要在表单中添加新频段,则效果很好,但是,如果用户没有添加新频段,则表示提交表单。它会中断,因为控制器1.times {@event.bands.build}
中的行会构建一个新的Band,但提交的表单没有名称来传递Band验证。
这似乎是为了工作,我需要一种方法来切换1.times {@event.bands.build}
我的表单的fields_for
部分切换,但我不认为这甚至是可能。
我尝试在事件模型中使用reject_if
:accepts_nested_attributes_for :bands, reject_if: proc { |attributes| attributes['name'].blank? }
,但它不起作用。我的搜索让我觉得这是失败的,因为reject_if
在模型保存之后才会运行,但Band.name上的验证会在保存之前运行吗?
现在,我将所有测试都放在我的控制器中:
def create
@event = current_user.events.build(event_params)
@event.bands.each do |band|
if band.name.blank?
band.destroy
end
end
if @event.save
blablabla
但这似乎与整个瘦小的控制器,胖模型方法相悖。
答案 0 :(得分:1)
一个简单的解决方法:
对强力参数使用两种不同的定义。一个包括和一个不包括乐队的参数。
def event_params
params.require(:event).permit(:name, :field1, ..., :fieldn)
end
def event_params_with_band
params.require(:event).permit(:name, :field1, ..., :fieldn, band_attributes: [:name, ..])
end
在@event = current_user.events.build(event_params)
之前应用一些逻辑来合并或不合并波段参数。例如:
def create
if params[:event][:new_band]=="true" # let's assume this comes from a 'tick' in your form
myparams=event_params_with_band
else
myparams=event_params
end
@event = current_user.events.build(myparams)
if @event.save
# your responses etc
end
end
我想你仍然需要建立event_bands的关系,但我想你知道如何做到这一点。