使用 button_to 进行更新操作时无法满足的强参数

Unsatisfiable strong params when using button_to for an update action

我无法理解在使用 button_to 执行 update 操作时如何满足强参数。我正在尝试将一个名为 active 的属性设置为 true 的值,用于一个名为 Plan 的 class 的现有实例。

(请注意,我在这里使用 HAML 来处理我的视图。)

这个有效:

= form_for(plan, remote: true) do |f|
  = f.hidden_field :active, value: true
  = f.submit 'set active'

但事实并非如此:

= button_to "set active", plan_path(plan, active: true), method: :put, remote: true

错误

Completed 400 Bad Request in 7ms (ActiveRecord: 1.1ms)

ActionController::ParameterMissing - param is missing or the value is empty: plan:
actionpack (4.2.1) lib/action_controller/metal/strong_parameters.rb:249:in 'require'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:77:in 'plan_params'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:45:in 'block in update'
actionpack (4.2.1) lib/action_controller/metal/mime_responds.rb:210:in 'respond_to'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:44:in 'update'

路线

          user_plans GET    /users/:user_id/plans(.:format)               plans#index
                     POST   /users/:user_id/plans(.:format)               plans#create
       new_user_plan GET    /users/:user_id/plans/new(.:format)           plans#new
           edit_plan GET    /plans/:id/edit(.:format)                     plans#edit
                plan PATCH  /plans/:id(.:format)                          plans#update
                     PUT    /plans/:id(.:format)                          plans#update
                     DELETE /plans/:id(.:format)                          plans#destroy

控制器

# PATCH/PUT /plans/1
def update
  respond_to do |format|
    if @plan.update(plan_params)
      format.js { flash.now[:notice] = "Plan was successfully updated." }
    end
  end
end

private 

def plan_params
  params.require(:plan).permit(:user_id, :name, :active)
end

这似乎是个愚蠢的问题,但我想不通,而且 the API documentation 似乎没有提供任何线索说明它为什么不起作用。

这些只是我尝试过的一些变体(每个变体后都有其随附的错误消息):

= button_to "set active", plan_path(plan: plan, active: true), method: :put, remote: true

ActionController::UrlGenerationError - No route matches {:action=>"update", :active=>true, :controller=>"plans", :plan=>#, :user_id=>"104"} missing required keys: [:id]:

= button_to "set active", plan_path(id: plan.id, active: true), method: :put, remote: true

Completed 400 Bad Request in 17ms (ActiveRecord: 2.1ms)

ActionController::ParameterMissing - param is missing or the value is empty: plan: actionpack (4.2.1) lib/action_controller/metal/strong_parameters.rb:249:in `require'

= button_to "set active", plan, active: true, method: :put, remote: true

ActionController::ParameterMissing - param is missing or the value is empty: plan: actionpack (4.2.1) lib/action_controller/metal/strong_parameters.rb:249:in 'require'
() Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:77:in 'plan_params' () Users/Rob/Sites/drexel_msis_planner/app/controllers/plans_controller.rb:45:in 'block in update'

根据http://apidock.com/rails/ActionView/Helpers/UrlHelper/button_toparams应该是一个单独的散列

= button_to "set active", plan, method: :put, remote: true, params: { :active => true }

如果您查看提交的参数,不同之处在于您的表单导致 params 成为

{ "id" => 123, "plan" => {"active" => true}, "controller" => "...", "action" => "..."}

而第二个结果是

{ "id" => 123, "active" => true, "controller" => "...", "action" => "..."}

在那种情况下 params[:plan] 为零,这会导致您看到错误。

有多种方法可以解决这个问题。您可以更改提交的参数以匹配控制器当前期望的参数,例如

button_to set_active, plan, method: :put, remote: true, params: {"plan[active]" => 1}

(您也可以让参数成为表单 URL 的一部分,就像您尝试的那样,但对我来说,作为表单字段感觉稍微更正确)。

或者,如果此更新操作未被任何其他表单使用,则更改它以匹配提交的数据。我通常不会这样做 - 这会很容易,但如果事情以可预测的方式运行,你的应用程序会更容易考虑。

根据this thread中的信息,我终于解决了这个问题。

我没有将参数作为 button_to 的另一个参数放在它们自己的散列中,而是将它们包含在对 plan_path 方法的调用中。第一个参数需要是模型的 ID,第二个参数需要是模型的名称作为键,所需属性的散列作为其值。 (以下示例):

= button_to "set active", plan_path(plan.id, plan: { active: true }), method: :put, remote: true