Rails 使用一个提交按钮提交多个嵌套表单
Rails submit multiple nested forms with one submit button
我正在为竞赛排行榜构建一个 rails 应用程序。数据模型为
class Tournament
has_many :events
end
class User
has_many :entries
has_many :events, through: :entries
end
class Events
has_many :entries
belongs_to :tournament
end
class Entry
belongs_to :user
belongs_to :event
end
每个锦标赛有 2 个赛事,每个赛事用户最多可以输入 4 个参赛作品,因此每个锦标赛总共有 8 个参赛作品。我想允许用户使用一个提交按钮在一个表单中输入所有 8 个条目。
我的路线
resources :entries, except: [:index, :show] do
collection do
match 'create_collection', via: [:create]
end
end
我在 Entries 控制器中也有一个 create_collection 方法来处理条目,但我还没有做到这一点。我不确定如何让表格正常工作。
_form.html.haml(由 views/tournaments/index.html.haml 渲染并传递 next_events,这是锦标赛的 2 个事件实例。
.entry-form
= form_tag create_collection_entries_path do |form|
- next_events.each do |event|
= event.name
= fields_for "events[]", event do |f|
- 4.times do
= f.fields_for :entries do |f|
= f.label_tag 'player'
= f.text_field 'player'
= submit_tag "Submit", class: "btn btn-success"
表单显示如我所料,但是当我单击提交按钮时,只有最后 4 个条目在参数中提交并且事件 ID 未被传递
"events"=>[{"entries_attributes"=>
{"0"=>{"player"=>"player5","id"=>"33"},
"1"=>{"player"=>"player6", "id"=>"34"},
"2"=>{"player"=>"player7", "id"=>"35"},
"3"=>{"player"=>"player8", "id"=>"36"}}}],
"commit"=>"Submit"}
如何使用正确的事件 ID 获取要提交的两个事件的所有入口参数?
我的研究表明 rails 没有提供一种方法,可以通过一次提交以一种形式提交模型的多个实例。我查看了 this and this,这对我来说不太适用。所以我自己滚了。
以相当易于管理的方式提交参数哈希中所有值的表单。这不是最优雅的解决方案,需要在后端进行大量处理。但是我找不到更 railsy 的方式来做到这一点。
form.html.haml
.entry-form.on
= form_tag create_collection_entries_path(@user.id) do
- Tournament.next.events.each do |event|
%div= event.name
%div
- @num_of_entry_forms.times do |n|
%div
= label_tag 'player'
= text_field_tag "event[#{event.id}][entries][player_#{n}]"
= submit_tag "Submit", class: "btn btn-success"
routes.rb
resources :entries, except: [:index, :show] do
collection do
get '/new_collection/:user_id', to: 'entries#new_collection', as: 'new_collection'
post '/create_collection/:user_id', to: 'entries#create_collection', as: 'create_collection'
end
end
entries_controller.rb
def new_collection
@num_of_entry_forms = EventEntryCollectionCreator::MAX_ENTRIES_PER_EVENT
@user = User.find(params[:user_id])
end
def create_collection
@eec = EventEntryCollection.new(params)
if @eec.save
redirect_to root_path
else
render :new_collection
end
end
我仍在研究作为服务模型的 EventEntryCollectionCreator,但这是它的核心。
MAX_ENTRIES_PER_EVENT = 4
def save
params["event"].each do |event|
event.last["entries"]["player"].each do |entry|
entry = Entry.new(event_id: event.first, user_id: user_id, player: entry.last)
@entries_collection << entry
end
end
save_collection
end
private
def save_collection
@entries_collection.each do |entry|
if valid_entry?
entry.save
@valid_models << entry
else
@invalid_models << entry
end
end
end
正如我所说,这不是最优雅的解决方案,但它解决了问题。好处是杂乱的后端处理从控制器中抽象出来,进入它自己的服务模型。消极方面是有一些代码味道,很多强制值进入散列和对象必须了解太多关于散列结构的信息。
我对其他解决方案或对正在进行的工作的改进持开放态度。
我正在为竞赛排行榜构建一个 rails 应用程序。数据模型为
class Tournament
has_many :events
end
class User
has_many :entries
has_many :events, through: :entries
end
class Events
has_many :entries
belongs_to :tournament
end
class Entry
belongs_to :user
belongs_to :event
end
每个锦标赛有 2 个赛事,每个赛事用户最多可以输入 4 个参赛作品,因此每个锦标赛总共有 8 个参赛作品。我想允许用户使用一个提交按钮在一个表单中输入所有 8 个条目。
我的路线
resources :entries, except: [:index, :show] do
collection do
match 'create_collection', via: [:create]
end
end
我在 Entries 控制器中也有一个 create_collection 方法来处理条目,但我还没有做到这一点。我不确定如何让表格正常工作。
_form.html.haml(由 views/tournaments/index.html.haml 渲染并传递 next_events,这是锦标赛的 2 个事件实例。
.entry-form
= form_tag create_collection_entries_path do |form|
- next_events.each do |event|
= event.name
= fields_for "events[]", event do |f|
- 4.times do
= f.fields_for :entries do |f|
= f.label_tag 'player'
= f.text_field 'player'
= submit_tag "Submit", class: "btn btn-success"
表单显示如我所料,但是当我单击提交按钮时,只有最后 4 个条目在参数中提交并且事件 ID 未被传递
"events"=>[{"entries_attributes"=>
{"0"=>{"player"=>"player5","id"=>"33"},
"1"=>{"player"=>"player6", "id"=>"34"},
"2"=>{"player"=>"player7", "id"=>"35"},
"3"=>{"player"=>"player8", "id"=>"36"}}}],
"commit"=>"Submit"}
如何使用正确的事件 ID 获取要提交的两个事件的所有入口参数?
我的研究表明 rails 没有提供一种方法,可以通过一次提交以一种形式提交模型的多个实例。我查看了 this and this,这对我来说不太适用。所以我自己滚了。
以相当易于管理的方式提交参数哈希中所有值的表单。这不是最优雅的解决方案,需要在后端进行大量处理。但是我找不到更 railsy 的方式来做到这一点。
form.html.haml
.entry-form.on
= form_tag create_collection_entries_path(@user.id) do
- Tournament.next.events.each do |event|
%div= event.name
%div
- @num_of_entry_forms.times do |n|
%div
= label_tag 'player'
= text_field_tag "event[#{event.id}][entries][player_#{n}]"
= submit_tag "Submit", class: "btn btn-success"
routes.rb
resources :entries, except: [:index, :show] do
collection do
get '/new_collection/:user_id', to: 'entries#new_collection', as: 'new_collection'
post '/create_collection/:user_id', to: 'entries#create_collection', as: 'create_collection'
end
end
entries_controller.rb
def new_collection
@num_of_entry_forms = EventEntryCollectionCreator::MAX_ENTRIES_PER_EVENT
@user = User.find(params[:user_id])
end
def create_collection
@eec = EventEntryCollection.new(params)
if @eec.save
redirect_to root_path
else
render :new_collection
end
end
我仍在研究作为服务模型的 EventEntryCollectionCreator,但这是它的核心。
MAX_ENTRIES_PER_EVENT = 4
def save
params["event"].each do |event|
event.last["entries"]["player"].each do |entry|
entry = Entry.new(event_id: event.first, user_id: user_id, player: entry.last)
@entries_collection << entry
end
end
save_collection
end
private
def save_collection
@entries_collection.each do |entry|
if valid_entry?
entry.save
@valid_models << entry
else
@invalid_models << entry
end
end
end
正如我所说,这不是最优雅的解决方案,但它解决了问题。好处是杂乱的后端处理从控制器中抽象出来,进入它自己的服务模型。消极方面是有一些代码味道,很多强制值进入散列和对象必须了解太多关于散列结构的信息。
我对其他解决方案或对正在进行的工作的改进持开放态度。