我一次提交包含 2-4 个对象的表单,具体取决于家长拥有的对象数量。我意识到这可能是非常规的,但我真的希望用户能够在一个表单上同时编辑所有对象。在我的表格上,我正在做:
<%= simple_fields_for "derps[]", derp do |f| %>
<% end %>
然后我在控制器中执行此操作:
def update
@derps = []
@rejects = []
derps_params.each do |key, hash|
derp = Derp.find(key)
derp.assign_attributes(hash)
@rejects << derp unless derp.save
end
if @rejects.empty?
redirect_to @parent, flash: {success: 'Derps were successfully updated.'}
else
@derps = @rejects
render :edit
end
end
假设有两个对象 - 参数如下:
"derps"=>{"1"=>{"attribute"=>"39", "another_attribute"=>"serp", "a_third_attribute"=>"yerp"}, "2"=>{"attribute"=>"30", "another_attribute"=>"49", }}
我在 Rails 3 中使用了这个,没有强参数。我正在升级到 Rails 4,并且正在努力解决如何使其正常工作的问题 - 我不断收到“未经允许的参数:1, 2”
我假设我需要做类似的事情:
def mashes_params
params.require(:derps).permit(
id: []
或
def mashes_params
params.require(:derps).permit(
:id,
沿着这些思路,但我已经尝试了我能想到的所有方法,但没有运气。
这里有什么想法吗?
我发现命令行对于调试 Rails 4 中的强参数非常有帮助。以下是我在控制台中测试您的问题的方法:
rails c # From within your project directory, short for 'rails console'
params = ActionController::Parameters.new( { derps: { 1 => { attribute: 39, another_attribute: "serp" }, 2 => { attribute: 30, another_attribute: 49 } } } )
params # To make sure that the object looks the same
permitted = params.require( :derps ).permit( 1 => [ :attribute, :another_attribute ], 2 => [ :attribute, :another_attribute ] )
permitted # To see what you'd get back in your controller
希望使用这个工具,您将能够比反复试验更容易地调试我的答案未提供的任何内容。
最终编辑(希望):
必须从头开始重新考虑这一点。我得出的结论是:既然 :id 可以作为通配符,但不允许作为散列的键,为什么不总是将键设置为 1-4,这样我可以将它们显式列入白名单,然后从键中获取 ID -散列中的值,很像传统形式的嵌套吗?这就是我最终解决它的方法。这是我正在工作的最终实现:
<% i = @parent.derps.index(derp) + 1 %>
<%= simple_fields_for "derps[#{i}]", derp do |f| %>
<%= f.hidden_field :id, value: derp.id %>
<%= render "rest_of_the_fields" %>
<% end %>
然后在控制器中:
def update
@derps = []
@rejects = []
derp_params.each do |key, hash|
derp = Derp.find(hash.delete("id"))
derp.assign_attributes(hash)
@rejects << derp unless derp.save
end
if @rejects.empty?
redirect_to @parent, flash: {success: "Derps updated successfully."}
else
@derps = @rejects
render :edit
end
end
然后是强参数:
def derp_params
p = [:id, :attribute_1, :another_attribute, ...]
params.require(:derps).permit(
"1" => p, "2" => p, "3" => p, "4" => p
)
end
唷。希望这对某人有帮助。
我见过的绝对最佳解决方案是这里:
def product_params
properties_keys = params[:product].try(:fetch, :properties, {}).keys
params.require(:product).permit(:title, :description, properties: properties_keys)
end
我又做了一项更改来迭代未命名的键,因为我的
property_keys
有更多嵌套的键和值:
response_keys = params[:survey][:responses].try(:fetch, :properties, {}).keys
params.require(:survey).permit(responses: response_keys.map {|rk| [rk => [:question_id, :answer_id, :value]]})
这是我目前正在使用的方法。您可以像这样一一允许每个嵌套参数:
params = ActionController::Parameters.new(
"derps" => {
"1" => {
"attribute" => "39",
"another_attribute" => "serp",
"a_third_attribute" => "yerp"
},
"2" => {
"attribute" => "30",
"another_attribute" => "49"
}
}
)
# => <ActionController::Parameters {"derps"=>{"1"=>{"attribute"=>"39", "another_attribute"=>"serp", "a_third_attribute"=>"yerp"}, "2"=>{"attribute"=>"30", "another_attribute"=>"49"}}} permitted: false>
params.fetch(:derps).map do |i, attrs|
[
i,
ActionController::Parameters.new(attrs).permit(
:attribute,
:another_attribute,
:a_third_attribute,
)
]
end.to_h.with_indifferent_access
#=> {"1"=><ActionController::Parameters {"attribute"=>"39", "another_attribute"=>"serp", "a_third_attribute"=>"yerp"} permitted: true>, "2"=><ActionController::Parameters {"attribute"=>"30", "another_attribute"=>"49"} permitted: true>}
这是一种实现此目的的肮脏方法,它建立在 Greg Blass 的上述答案的基础上
这可以处理无限数量的带有嵌套参数的索引
def foo_bar_params
num_keys = params[:foo_bars].keys.size
the_params = [:id, :attr1, :attr2, :another]
permit_hash = {}
i = 0
while i < num_entries
permit_hash[i.to_s] = the_params
i += 1
end
params.require(:foo_bars).permit(permit_hash)
end
我确信有一种更奇特的方法可以做到这一点,但这种方法是可读的,我可以轻松地知道发生了什么......最重要的是它有效
在控制器中,迄今为止我找到的最干净的解决方案是:
derp_params = params
.permit(:derp => [:attribute, :another_attribute])
.fetch(:derp) { {} }
.values
如果您确实希望在密钥丢失时引发异常,请先调用
#require
或在 #fetch
中引发异常。
params.require(:derp)
derp_params = params
.permit(:derp => [:attribute, :another_attribute])
.fetch(:derp) { {} }
.values
# or
derp_params = params
.permit(:derp => [:attribute, :another_attribute])
.fetch(:derp) { |key| raise ActionController::ParameterMissing, key, params.keys }
.values
当然,如果你经常使用这种模式,你可以将所有这些包装在 ApplicationController 中的一个方便的方法中。
额外提示:
fields_for
表单助手接受:index
选项。
<% derps.each_with_index do |derp, index| %>
<%= fields_for 'derps', derp, index: index do |f| %>
<%= f.text_field :attribute %>
<% end %>
<% end %>
# Will produce:
# <input name="derps[0][attribute]" ...etc.