Rails 5 使用 Wicked 的多步骤表单 Gem 出现 Forbidden Method 错误

Rails 5 Multi-Step form using Wicked Gem is getting Forbidden Method error

一直在尝试弄清楚如何为用户将使用 Wicked Wizard 构建的对象获取多步骤表单 gem。我一直在关注 gem 的 github 和这个 question on that Scheem's answered himself, but I am getting a forbidden method error when on the last step of the wizard and am trying to submit. I've seen in other questions, to refer to strong parameters 上的文档,我也一直在使用它,但不确定是什么导致了错误。到目前为止,我有以下内容。

错误信息:

ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):

app/controllers/places/steps_controller.rb:14:in `update'
Started GET "/places/20/steps/id" for 127.0.0.1 at 2017-07-02 03:10:57 -0400
Processing by Places::StepsController#show as HTML
  Parameters: {"place_id"=>"20", "id"=>"id"}
  Place Load (183.0ms)  SELECT  "places".* FROM "places" WHERE "places"."id" = ? LIMIT ?  [["id", 20], ["LIMIT", 1]]
  Rendering places/steps/id.html.erb within layouts/application
  Rendered places/steps/id.html.erb within layouts/application (851.5ms)
Completed 200 OK in 5753ms (Views: 4688.2ms | ActiveRecord: 183.0ms)


Started PUT "/places/20/steps/id" for 127.0.0.1 at 2017-07-02 03:11:24 -0400
Processing by Places::StepsController#update as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"/VrDWevWLY+A6841zIR5awBGopfnoXWxE8zf+6xkogJC6ufPQ+qYGp4Mg72N3Dlc547oSF/cXa4c/vMK+JU6qg==", "place"=>{"cost"=>"0.00", "user_id"=>"1"}, "commit"=>"Complete", "place_id"=>"20", "id"=>"id"}
  Place Load (14.9ms)  SELECT  "places".* FROM "places" WHERE "places"."id" = ? LIMIT ?  [["id", 20], ["LIMIT", 1]]
   (34.9ms)  begin transaction
   (0.1ms)  rollback transaction
Completed 500 Internal Server Error in 212ms (ActiveRecord: 49.8ms)



ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):

app/controllers/places/steps_controller.rb:14:in `update'

Schema.rb

ActiveRecord::Schema.define(version: 20170630190146) do

  create_table "places", force: :cascade do |t|
    t.string "name"
    t.string "location"
    t.string "cost"
    t.date "available"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "user_id"
    t.string "status"
    t.index ["user_id"], name: "index_places_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "username"
    t.string "password_digest"
  end

end

place.rb

class Place < ApplicationRecord
    belongs_to :user

    validates :name, :location, :cost, :available, :presence => true, :if => :active?

  def active?
    status == 'active'      
  end

end

places_controller.rb

class PlacesController < ApplicationController
  before_action :set_place, only: [:show, :edit, :update, :destroy]

  # GET /places
  # GET /places.json
  def index
    @places = Place.where.not(name: nil)
  end

  # GET /places/1
  # GET /places/1.json
  def show
  end

  # GET /places/new
  def new
    @place = Place.new
  end

  # GET /places/1/edit
  def edit
  end

  # POST /places
  # POST /places.json
  def create
    @place = Place.new
    if @place.save(validate: false)
      redirect_to place_step_path(place_id: @place.id, id: :date)
    else
      render new
    end
  end

  # PATCH/PUT /places/1
  # PATCH/PUT /places/1.json
  def update
    respond_to do |format|
      if @place.update(place_params)
        format.html { redirect_to @place, notice: 'Place was successfully updated.' }
        format.json { render :show, status: :ok, location: @place }
      else
        format.html { render :edit }
        format.json { render json: @place.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /places/1
  # DELETE /places/1.json
  def destroy
    @place.destroy
    respond_to do |format|
      format.html { redirect_to places_url, notice: 'Place was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_place
      @place = Place.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def place_params
      params.require(:place).permit(:name, :location, :cost, :available, :user_id, :status)
    end
end

steps_controller.rb

class Places::StepsController < ApplicationController
  include Wicked::Wizard

  steps :date, :id

  def show
    @place = Place.find(params[:place_id])
    render_wizard
  end

  def update
    @place = Place.find(params[:place_id])
    params[:place][:status] = 'active' if step == steps.last
    @place.update_attributes(params[:place])
    render_wizard @place
  end

  def create
    @place = Place.create
    redirect_to wizard_path(steps.first, :place_id => @product.id)

  end


private

end

routes.rb

Rails.application.routes.draw do

  root 'static_pages#home'
  resources :places
  resources :places do
    resources :steps, controller: 'places/steps'
  end


  resources :users



  # session routes
  resources :sessions
  get 'session/new'
  get '/sessions/new', to: 'sessions#new'
  post '/sessions/new', to: 'sessions#create'
  get '/logout', to: 'sessions#destroy'
  delete '/logout', to: 'sessions#destroy'

end

date.html.erb

<%= form_for @place, method: :put, url: wizard_path do |f| %>
  <% if f.object.errors.any? %>
    <div class="error-messages">
        <% f.object.errors.full_messages.each do |error| %>
          <p><%= error %></p>
          <% end %>
    </div>
  <% end %>
  <div class="h2">When Is Place available?</div>
  <div class="fied">
    <%= f.label "Date" %>
    <%= f.date_select :available, start_year: 2017 %>
  </div>
  <div class="actions">
    <%= f.submit "Next" %>
    or <%= link_to "skip this step", next_wizard_path %>
  </div>
<% end %>

id.html.erb

<%= form_for @place, method: :put, url: wizard_path do |f| %>
  <% if f.object.errors.any? %>
    <div class="error-messages">
      <% f.object.errors.full_messages.each do |error| %>
        <p><%= error %></p>
      <% end %>
    </div>
  <% end %>
  <div class="h2">
    <div class="field">
      <%= f.label "Cost" %>
      <%= f.text_field :cost %>
    </div>
    <div class="field">
      <%= f.label "Belongs to" %>
      <%= f.text_field :user_id %>
    </div>
  </div>

  <div class="actions">
    <%= f.submit "Complete" %>
  </div>
<% end %>

ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError)

我认为 Places::StepsController 中的 update 方法是罪魁祸首。您在那个方法中没有使用 strong params。尝试将其更改为以下

def update
  @place = Place.find(params[:place_id])
  params[:place][:status] = 'active' if step == steps.last
  @place.update_attributes(place_params) #here you should use strong params
  render_wizard @place
end

并在private

下定义一个place_params
private
def place_params
  params.require(:place).permit(:name, :location, :cost, :available, :user_id, :status)
end