强参数 – 无法访问深层嵌套属性

一个StackOverflow问题询问了我需要什么,但自我答案并没有帮助我知道下一步该做什么。 在那个问题中呈现的场景( 在铁轨中将深层嵌套的强参数列入白名单 )几乎就是我所发生的事情,但我会发布一个我的缩写(仍然很长)并希望有人 – 甚至可能是戴夫post – 可以帮助(我没有足够的声誉评论和问他)。 关于嵌套的强参数的链接很少,我还没有读过,我在许多控制器和API端点上处理了一些,但这是应用程序中最复杂的。 (我包含了这么长的例子,所以你可以看到完整的复杂性。)

这是在sales_controller ,我们无法获得的属性之一是timezone_name ,它位于run_spans_attributes ,位于sale下的options_attributes 。 我已经尝试了几乎所有与嵌入式属性匹配的不同语法方法,这些方法在StackOverflow上有强参数问题,但没有一种方法有效。 我需要更多课程吗? 有魔术括号吗? 我需要新的建议。 请。

应该注意的是,这是使用strong_parameters gem和Rails 3.2.21,但我希望为Rails 4准备应用程序,所以我希望避免短期解决方案。

对不起,这么久了:

  Parameters: "sale"=>{ "cloned_from"=>"", "type"=>"Localsale", "primary_contact_attributes"=>{ "primary"=>"true", "first_name"=>"Fred", "id"=>"1712" }, "contract_signed_on"=>"March 20, 2015", "billing_addresses_attributes"=>{ "0"=>{ "billing"=>"1", "city"=>"San Diego", "id"=>"29076" } }, "other_contacts_attributes"=>{ "0"=>{ "first_name"=>"Fred", "_destroy"=>"false", "id"=>"170914" }, "1"=>{ "first_name"=>"Fred", "last_name"=>"Smith", "_destroy"=>"false", "id"=>"1798" } }, "opportunity_detail_attributes"=>{ "original_salesperson_id"=>"", "id"=>"10130" }, "production_editor"=>"1868097", "event_sale_attributes"=>{ "0"=>{ "name"=>"is_super_sale", "value"=>"0", "id"=>"15326" }, "1"=>{ "name"=>"super_show_code", "value"=>"" }, }, "scheduling_note"=>"", "category_ids"=>["2", "364"], "options_attributes"=>{ "0"=>{ "title"=>"T-Shirt and Bag Check", "event_starts_at(1i)"=>"2015", "event_starts_at(2i)"=>"6", "event_doors_open_at_attributes"=>{ "option_id"=>"8682604", "doors_time(1i)"=>"", "id"=>"278382" }, "event_option_attributes"=>{ "0"=>{ "name"=>"event_duration", "value"=>"" }, "1"=>{ "name"=>"send_pre_event_email", "value"=>"1", "id"=>"632546" } }, "language_id"=>"1", "run_spans_attributes"=>{ "0"=>{ "timezone_name"=>"Eastern Time (US & Canada)", "_destroy"=>"false", "id"=>"560878" }, "1429320288130"=>{ "timezone_name"=>"Eastern Time (US & Canada)", "_destroy"=>"false" } }, "_destroy"=>"false", "id"=>"8682604" }#ends 0 option },#ends options "coupons_per_redemption"=>"1", "methods_attributes"=>{ "0"=>{ "redemption_code"=>"0", "_destroy"=>"0", "id"=>"9797012" }, "1"=>{ "redemption_code"=>"4", "_destroy"=>"1", "vendor_provided_promo_code"=>"0", "promo_code"=>"" } }, #ends redemption methods "addresses_attributes"=>{ "0"=>{ "street_address_1"=>"2400 Cat St", "primary"=>"0", "id"=>"2931074", "_destroy"=>"false" } }, "zoom"=>"", "video_attributes"=>{ "youtube_id"=>"", }, "updated_from"=>"edit" } 

帮帮我这么做吧? 顺便说一下,各种.tap do |whitelisted| 方法失败了。

  private def_sale_strong_params params.require(:sale).permit(:how, :the, :heck, :do, :the_attributes => [:make, themselves => [:known, :outside => [:of, :these => [:darn, :parentheses], :and], :brackets]]) end 

1.在控制台中validation您的进度

要配置强参数,它可以帮助在Rails控制台中工作。 您可以将参数设置为ActiveSupport::HashWithIndifferentAccess对象,然后开始测试从ActionController::Parameters

例如,假设我们从:

 params = ActiveSupport::HashWithIndifferentAccess.new( "sale"=>{ "cloned_from"=>"", "type"=>"Localsale"} ) 

我们可以运行这个:

 ActionController::Parameters.new(params).require(:sale).permit(:cloned_from, :type) 

我们将此作为返回值,我们将知道我们已成功允许cloned_fromtype

 {"cloned_from"=>"", "type"=>"Localsale"} 

您可以继续工作直到考虑所有参数。 如果您包含问题中的整个参数集……

 params = ActiveSupport::HashWithIndifferentAccess.new( "sale"=>{ "cloned_from"=>"", "type"=>"Localsale", ... "options_attributes"=>{ "0"=>{ "title"=>"T-Shirt and Bag Check", ... "run_spans_attributes"=>{ "0"=>{ "timezone_name"=>"Eastern Time (US & Canada)", ... ) 

…你可以得到一个结构看起来像这样的timezone_name

 ActionController::Parameters.new(params).require(:sale).permit(:cloned_from, :type, options_attributes: [:title, run_spans_attributes: [:timezone_name]]) 

控制台中的返回值为:

 {"cloned_from"=>"", "type"=>"Localsale", "options_attributes"=>{"0"=>{"title"=>"T-Shirt and Bag Check", "run_spans_attributes"=>{"0"=>{"timezone_name"=>"Eastern Time (US & Canada)"}, "1429320288130"=>{"timezone_name"=>"Eastern Time (US & Canada)"}}}}} 

2.将工作分解为更小的部分

尝试在一行上处理每个模型的所有允许属性会让人感到困惑。 您可以将其分解为多行,以使事情更容易理解。 从这个结构开始,并为每个模型填写其他属性:

 video_attrs = [] # Fill in these empty arrays with the allowed parameters for each model addresses_attrs = [] methods_attrs = [] run_spans_attrs = [:timezone_name] event_option_attrs = [] options_attrs = [:title, event_option_attributes: event_option_attrs, run_spans_attributes: run_spans_attrs] event_sale_attrs = [] opportunity_detail_attrs = [] other_contacts_attrs = [] billing_addresses_attrs = [] primary_contact_attrs = [] sales_attributes = [ :cloned_from, :type, primary_contact_attributes: primary_contact_attrs, billing_addresses_attributes: billing_addresses_attrs, other_contacts_attributes: other_contacts_attrs, options_attributes: options_attrs, opportunity_detail_attributes: opportunity_detail_attrs, event_sale_attributes: event_sale_attrs, methods_attributes: methods_attrs, addresses_attributes: addresses_attrs, video_attributes: video_attrs ] 

然后,您可以将*sales_attributes发送到允许的参数中。 您可以在控制台中validation:

 ActionController::Parameters.new(params).require(:sale).permit(*sales_attributes) 

试试这个:

 params.require(:sale).permit(:options_attributes => [:other_attributes, { :run_spans_attributes => [:timezone_name] }] 

按照我在控制器中所做的,这是我认为可行的:

 params.require(:sale).permit(:cloned_form, ... billing_addresses_attributes: [:id, :city, :building] ...) 

有条不紊地,在“0”=>等属性周围放置括号,并且不要忘记adf:id和:_destroy,就像我在回答中提到的那样,因为我最终创建了其他模型并使用了accepts_nested_attributes_for。

对于后人,我想让大家知道我们最终发现了什么:嗯,信息就在那里,事实上,我在Russ Olsen的Eloquent Ruby中读到了这一点,我感谢在一个更高级的开发人员的调试会议期间记得:然而,随着Ruby 1.9的出现,哈希已经变得坚定有序。“

为什么这么重要? 我将控制器上的属性按字母顺序排列,并且由于run_span_attributes的设置方式,他们需要将timezone_name放在前面。 如果不是,那就无法实现。 你只能想象调试这会如何折磨我们。 但至少我们现在知道了:订购事项。 所以,如果一切都失败了,请记住。